Roll src/third_party/WebKit f298044:aa8346d (svn 202628:202629)
[chromium-blink-merge.git] / chrome / browser / download / download_target_determiner.cc
blob99bcd7f1e855e09c96b958c6277aeda025c8f3dd
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/location.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/rand_util.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/time/time.h"
14 #include "chrome/browser/download/chrome_download_manager_delegate.h"
15 #include "chrome/browser/download/download_crx_util.h"
16 #include "chrome/browser/download/download_extensions.h"
17 #include "chrome/browser/download/download_prefs.h"
18 #include "chrome/browser/history/history_service_factory.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "components/history/core/browser/history_service.h"
23 #include "components/mime_util/mime_util.h"
24 #include "content/public/browser/browser_context.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/download_interrupt_reasons.h"
27 #include "extensions/common/constants.h"
28 #include "net/base/filename_util.h"
29 #include "ui/base/l10n/l10n_util.h"
31 #if defined(ENABLE_EXTENSIONS)
32 #include "chrome/browser/extensions/webstore_installer.h"
33 #include "extensions/common/feature_switch.h"
34 #endif
36 #if defined(ENABLE_PLUGINS)
37 #include "chrome/browser/plugins/plugin_prefs.h"
38 #include "content/public/browser/plugin_service.h"
39 #include "content/public/common/webplugininfo.h"
40 #endif
42 #if defined(OS_WIN)
43 #include "chrome/browser/ui/pdf/adobe_reader_info_win.h"
44 #endif
46 using content::BrowserThread;
47 using content::DownloadItem;
49 namespace {
51 const base::FilePath::CharType kCrdownloadSuffix[] =
52 FILE_PATH_LITERAL(".crdownload");
54 // Condenses the results from HistoryService::GetVisibleVisitCountToHost() to a
55 // single bool. A host is considered visited before if prior visible visits were
56 // found in history and the first such visit was earlier than the most recent
57 // midnight.
58 void VisitCountsToVisitedBefore(
59 const base::Callback<void(bool)>& callback,
60 bool found_visits,
61 int count,
62 base::Time first_visit) {
63 callback.Run(
64 found_visits && count > 0 &&
65 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight()));
68 #if defined(OS_WIN)
69 // Keeps track of whether Adobe Reader is up to date.
70 bool g_is_adobe_reader_up_to_date_ = false;
71 #endif
73 } // namespace
75 DownloadTargetInfo::DownloadTargetInfo()
76 : is_filetype_handled_safely(false) {}
78 DownloadTargetInfo::~DownloadTargetInfo() {}
80 DownloadTargetDeterminerDelegate::~DownloadTargetDeterminerDelegate() {
83 DownloadTargetDeterminer::DownloadTargetDeterminer(
84 DownloadItem* download,
85 const base::FilePath& initial_virtual_path,
86 DownloadPrefs* download_prefs,
87 DownloadTargetDeterminerDelegate* delegate,
88 const CompletionCallback& callback)
89 : next_state_(STATE_GENERATE_TARGET_PATH),
90 should_prompt_(false),
91 should_notify_extensions_(false),
92 create_target_directory_(false),
93 conflict_action_(DownloadPathReservationTracker::OVERWRITE),
94 danger_type_(download->GetDangerType()),
95 is_dangerous_file_(false),
96 virtual_path_(initial_virtual_path),
97 is_filetype_handled_safely_(false),
98 download_(download),
99 is_resumption_(download_->GetLastReason() !=
100 content::DOWNLOAD_INTERRUPT_REASON_NONE &&
101 !initial_virtual_path.empty()),
102 download_prefs_(download_prefs),
103 delegate_(delegate),
104 completion_callback_(callback),
105 weak_ptr_factory_(this) {
106 DCHECK_CURRENTLY_ON(BrowserThread::UI);
107 DCHECK(download_);
108 DCHECK(delegate);
109 download_->AddObserver(this);
111 DoLoop();
114 DownloadTargetDeterminer::~DownloadTargetDeterminer() {
115 DCHECK_CURRENTLY_ON(BrowserThread::UI);
116 DCHECK(download_);
117 DCHECK(completion_callback_.is_null());
118 download_->RemoveObserver(this);
121 void DownloadTargetDeterminer::DoLoop() {
122 Result result = CONTINUE;
123 do {
124 State current_state = next_state_;
125 next_state_ = STATE_NONE;
127 switch (current_state) {
128 case STATE_GENERATE_TARGET_PATH:
129 result = DoGenerateTargetPath();
130 break;
131 case STATE_NOTIFY_EXTENSIONS:
132 result = DoNotifyExtensions();
133 break;
134 case STATE_RESERVE_VIRTUAL_PATH:
135 result = DoReserveVirtualPath();
136 break;
137 case STATE_PROMPT_USER_FOR_DOWNLOAD_PATH:
138 result = DoPromptUserForDownloadPath();
139 break;
140 case STATE_DETERMINE_LOCAL_PATH:
141 result = DoDetermineLocalPath();
142 break;
143 case STATE_DETERMINE_MIME_TYPE:
144 result = DoDetermineMimeType();
145 break;
146 case STATE_DETERMINE_IF_HANDLED_SAFELY_BY_BROWSER:
147 result = DoDetermineIfHandledSafely();
148 break;
149 case STATE_DETERMINE_IF_ADOBE_READER_UP_TO_DATE:
150 result = DoDetermineIfAdobeReaderUpToDate();
151 break;
152 case STATE_CHECK_DOWNLOAD_URL:
153 result = DoCheckDownloadUrl();
154 break;
155 case STATE_DETERMINE_INTERMEDIATE_PATH:
156 result = DoDetermineIntermediatePath();
157 break;
158 case STATE_CHECK_VISITED_REFERRER_BEFORE:
159 result = DoCheckVisitedReferrerBefore();
160 break;
161 case STATE_NONE:
162 NOTREACHED();
163 return;
165 } while (result == CONTINUE);
166 // Note that if a callback completes synchronously, the handler will still
167 // return QUIT_DOLOOP. In this case, an inner DoLoop() may complete the target
168 // determination and delete |this|.
170 if (result == COMPLETE)
171 ScheduleCallbackAndDeleteSelf();
174 DownloadTargetDeterminer::Result
175 DownloadTargetDeterminer::DoGenerateTargetPath() {
176 DCHECK_CURRENTLY_ON(BrowserThread::UI);
177 DCHECK(local_path_.empty());
178 DCHECK(!should_prompt_);
179 DCHECK(!should_notify_extensions_);
180 DCHECK_EQ(DownloadPathReservationTracker::OVERWRITE, conflict_action_);
181 bool is_forced_path = !download_->GetForcedFilePath().empty();
183 next_state_ = STATE_NOTIFY_EXTENSIONS;
185 if (!virtual_path_.empty() && HasPromptedForPath() && !is_forced_path) {
186 // The download is being resumed and the user has already been prompted for
187 // a path. Assume that it's okay to overwrite the file if there's a conflict
188 // and reuse the selection.
189 should_prompt_ = ShouldPromptForDownload(virtual_path_);
190 } else if (!is_forced_path) {
191 // If we don't have a forced path, we should construct a path for the
192 // download. Forced paths are only specified for programmatic downloads
193 // (WebStore, Drag&Drop). Treat the path as a virtual path. We will
194 // eventually determine whether this is a local path and if not, figure out
195 // a local path.
196 std::string default_filename(
197 l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
198 base::FilePath generated_filename = net::GenerateFileName(
199 download_->GetURL(),
200 download_->GetContentDisposition(),
201 GetProfile()->GetPrefs()->GetString(prefs::kDefaultCharset),
202 download_->GetSuggestedFilename(),
203 download_->GetMimeType(),
204 default_filename);
205 should_prompt_ = ShouldPromptForDownload(generated_filename);
206 base::FilePath target_directory;
207 if (should_prompt_) {
208 DCHECK(!download_prefs_->IsDownloadPathManaged());
209 // If the user is going to be prompted and the user has been prompted
210 // before, then always prefer the last directory that the user selected.
211 target_directory = download_prefs_->SaveFilePath();
212 } else {
213 target_directory = download_prefs_->DownloadPath();
215 virtual_path_ = target_directory.Append(generated_filename);
216 #if defined(OS_ANDROID)
217 conflict_action_ = DownloadPathReservationTracker::PROMPT;
218 #else
219 conflict_action_ = DownloadPathReservationTracker::UNIQUIFY;
220 #endif
221 should_notify_extensions_ = true;
222 } else {
223 virtual_path_ = download_->GetForcedFilePath();
224 // If this is a resumed download which was previously interrupted due to an
225 // issue with the forced path, the user is still not prompted. If the path
226 // supplied to a programmatic download is invalid, then the caller needs to
227 // intervene.
229 DCHECK(virtual_path_.IsAbsolute());
230 DVLOG(20) << "Generated virtual path: " << virtual_path_.AsUTF8Unsafe();
232 // If the download is DOA, don't bother going any further. This would be the
233 // case for a download that failed to initialize (e.g. the initial temporary
234 // file couldn't be created because both the downloads directory and the
235 // temporary directory are unwriteable).
237 // A virtual path is determined for DOA downloads for display purposes. This
238 // is why this check is performed here instead of at the start.
239 if (download_->GetState() != DownloadItem::IN_PROGRESS)
240 return COMPLETE;
241 return CONTINUE;
244 DownloadTargetDeterminer::Result
245 DownloadTargetDeterminer::DoNotifyExtensions() {
246 DCHECK_CURRENTLY_ON(BrowserThread::UI);
247 DCHECK(!virtual_path_.empty());
249 next_state_ = STATE_RESERVE_VIRTUAL_PATH;
251 if (!should_notify_extensions_)
252 return CONTINUE;
254 delegate_->NotifyExtensions(download_, virtual_path_,
255 base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone,
256 weak_ptr_factory_.GetWeakPtr()));
257 return QUIT_DOLOOP;
260 void DownloadTargetDeterminer::NotifyExtensionsDone(
261 const base::FilePath& suggested_path,
262 DownloadPathReservationTracker::FilenameConflictAction conflict_action) {
263 DCHECK_CURRENTLY_ON(BrowserThread::UI);
264 DVLOG(20) << "Extension suggested path: " << suggested_path.AsUTF8Unsafe();
266 // Extensions should not call back here more than once.
267 DCHECK_EQ(STATE_RESERVE_VIRTUAL_PATH, next_state_);
269 if (!suggested_path.empty()) {
270 // If an extension overrides the filename, then the target directory will be
271 // forced to download_prefs_->DownloadPath() since extensions cannot place
272 // downloaded files anywhere except there. This prevents subdirectories from
273 // accumulating: if an extension is allowed to say that a file should go in
274 // last_download_path/music/foo.mp3, then last_download_path will accumulate
275 // the subdirectory /music/ so that the next download may end up in
276 // Downloads/music/music/music/bar.mp3.
277 base::FilePath new_path(download_prefs_->DownloadPath().Append(
278 suggested_path).NormalizePathSeparators());
279 // Do not pass a mime type to GenerateSafeFileName so that it does not force
280 // the filename to have an extension if the (Chrome) extension does not
281 // suggest it.
282 net::GenerateSafeFileName(std::string(), false, &new_path);
283 virtual_path_ = new_path;
284 create_target_directory_ = true;
286 // An extension may set conflictAction without setting filename.
287 if (conflict_action != DownloadPathReservationTracker::UNIQUIFY)
288 conflict_action_ = conflict_action;
290 DoLoop();
293 DownloadTargetDeterminer::Result
294 DownloadTargetDeterminer::DoReserveVirtualPath() {
295 DCHECK_CURRENTLY_ON(BrowserThread::UI);
296 DCHECK(!virtual_path_.empty());
298 next_state_ = STATE_PROMPT_USER_FOR_DOWNLOAD_PATH;
300 delegate_->ReserveVirtualPath(
301 download_, virtual_path_, create_target_directory_, conflict_action_,
302 base::Bind(&DownloadTargetDeterminer::ReserveVirtualPathDone,
303 weak_ptr_factory_.GetWeakPtr()));
304 return QUIT_DOLOOP;
307 void DownloadTargetDeterminer::ReserveVirtualPathDone(
308 const base::FilePath& path, bool verified) {
309 DCHECK_CURRENTLY_ON(BrowserThread::UI);
310 DVLOG(20) << "Reserved path: " << path.AsUTF8Unsafe()
311 << " Verified:" << verified;
312 DCHECK_EQ(STATE_PROMPT_USER_FOR_DOWNLOAD_PATH, next_state_);
314 should_prompt_ = (should_prompt_ || !verified);
315 virtual_path_ = path;
316 DoLoop();
319 DownloadTargetDeterminer::Result
320 DownloadTargetDeterminer::DoPromptUserForDownloadPath() {
321 DCHECK_CURRENTLY_ON(BrowserThread::UI);
322 DCHECK(!virtual_path_.empty());
324 next_state_ = STATE_DETERMINE_LOCAL_PATH;
326 if (should_prompt_) {
327 delegate_->PromptUserForDownloadPath(
328 download_,
329 virtual_path_,
330 base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone,
331 weak_ptr_factory_.GetWeakPtr()));
332 return QUIT_DOLOOP;
334 return CONTINUE;
337 void DownloadTargetDeterminer::PromptUserForDownloadPathDone(
338 const base::FilePath& virtual_path) {
339 DCHECK_CURRENTLY_ON(BrowserThread::UI);
340 DVLOG(20) << "User selected path:" << virtual_path.AsUTF8Unsafe();
341 if (virtual_path.empty()) {
342 CancelOnFailureAndDeleteSelf();
343 return;
345 DCHECK_EQ(STATE_DETERMINE_LOCAL_PATH, next_state_);
347 virtual_path_ = virtual_path;
348 download_prefs_->SetSaveFilePath(virtual_path_.DirName());
349 DoLoop();
352 DownloadTargetDeterminer::Result
353 DownloadTargetDeterminer::DoDetermineLocalPath() {
354 DCHECK_CURRENTLY_ON(BrowserThread::UI);
355 DCHECK(!virtual_path_.empty());
356 DCHECK(local_path_.empty());
358 next_state_ = STATE_DETERMINE_MIME_TYPE;
360 delegate_->DetermineLocalPath(
361 download_,
362 virtual_path_,
363 base::Bind(&DownloadTargetDeterminer::DetermineLocalPathDone,
364 weak_ptr_factory_.GetWeakPtr()));
365 return QUIT_DOLOOP;
368 void DownloadTargetDeterminer::DetermineLocalPathDone(
369 const base::FilePath& local_path) {
370 DCHECK_CURRENTLY_ON(BrowserThread::UI);
371 DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe();
372 if (local_path.empty()) {
373 // Path subsitution failed.
374 CancelOnFailureAndDeleteSelf();
375 return;
377 DCHECK_EQ(STATE_DETERMINE_MIME_TYPE, next_state_);
379 local_path_ = local_path;
380 DoLoop();
383 DownloadTargetDeterminer::Result
384 DownloadTargetDeterminer::DoDetermineMimeType() {
385 DCHECK_CURRENTLY_ON(BrowserThread::UI);
386 DCHECK(!virtual_path_.empty());
387 DCHECK(!local_path_.empty());
388 DCHECK(mime_type_.empty());
390 next_state_ = STATE_DETERMINE_IF_HANDLED_SAFELY_BY_BROWSER;
392 if (virtual_path_ == local_path_) {
393 delegate_->GetFileMimeType(
394 local_path_,
395 base::Bind(&DownloadTargetDeterminer::DetermineMimeTypeDone,
396 weak_ptr_factory_.GetWeakPtr()));
397 return QUIT_DOLOOP;
399 return CONTINUE;
402 void DownloadTargetDeterminer::DetermineMimeTypeDone(
403 const std::string& mime_type) {
404 DCHECK_CURRENTLY_ON(BrowserThread::UI);
405 DVLOG(20) << "MIME type: " << mime_type;
406 DCHECK_EQ(STATE_DETERMINE_IF_HANDLED_SAFELY_BY_BROWSER, next_state_);
408 mime_type_ = mime_type;
409 DoLoop();
412 #if defined(ENABLE_PLUGINS)
413 // The code below is used by DoDetermineIfHandledSafely to determine if the
414 // file type is handled by a sandboxed plugin.
415 namespace {
417 void InvokeClosureAfterGetPluginCallback(
418 const base::Closure& closure,
419 const std::vector<content::WebPluginInfo>& unused) {
420 closure.Run();
423 enum ActionOnStalePluginList {
424 RETRY_IF_STALE_PLUGIN_LIST,
425 IGNORE_IF_STALE_PLUGIN_LIST
428 void IsHandledBySafePlugin(content::ResourceContext* resource_context,
429 const GURL& url,
430 const std::string& mime_type,
431 ActionOnStalePluginList stale_plugin_action,
432 const base::Callback<void(bool)>& callback) {
433 DCHECK_CURRENTLY_ON(BrowserThread::IO);
434 DCHECK(!mime_type.empty());
435 using content::WebPluginInfo;
437 std::string actual_mime_type;
438 bool is_stale = false;
439 WebPluginInfo plugin_info;
441 content::PluginService* plugin_service =
442 content::PluginService::GetInstance();
443 bool plugin_found = plugin_service->GetPluginInfo(-1, -1, resource_context,
444 url, GURL(), mime_type,
445 false, &is_stale,
446 &plugin_info,
447 &actual_mime_type);
448 if (is_stale && stale_plugin_action == RETRY_IF_STALE_PLUGIN_LIST) {
449 // The GetPlugins call causes the plugin list to be refreshed. Once that's
450 // done we can retry the GetPluginInfo call. We break out of this cycle
451 // after a single retry in order to avoid retrying indefinitely.
452 plugin_service->GetPlugins(
453 base::Bind(&InvokeClosureAfterGetPluginCallback,
454 base::Bind(&IsHandledBySafePlugin,
455 resource_context,
456 url,
457 mime_type,
458 IGNORE_IF_STALE_PLUGIN_LIST,
459 callback)));
460 return;
462 // In practice, we assume that retrying once is enough.
463 DCHECK(!is_stale);
464 bool is_handled_safely =
465 plugin_found &&
466 (plugin_info.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS ||
467 plugin_info.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS ||
468 plugin_info.type == WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN);
469 BrowserThread::PostTask(
470 BrowserThread::UI, FROM_HERE, base::Bind(callback, is_handled_safely));
473 } // namespace
474 #endif // defined(ENABLE_PLUGINS)
476 DownloadTargetDeterminer::Result
477 DownloadTargetDeterminer::DoDetermineIfHandledSafely() {
478 DCHECK_CURRENTLY_ON(BrowserThread::UI);
479 DCHECK(!virtual_path_.empty());
480 DCHECK(!local_path_.empty());
481 DCHECK(!is_filetype_handled_safely_);
483 next_state_ = STATE_DETERMINE_IF_ADOBE_READER_UP_TO_DATE;
485 if (mime_type_.empty())
486 return CONTINUE;
488 if (mime_util::IsSupportedMimeType(mime_type_)) {
489 is_filetype_handled_safely_ = true;
490 return CONTINUE;
493 #if defined(ENABLE_PLUGINS)
494 BrowserThread::PostTask(
495 BrowserThread::IO,
496 FROM_HERE,
497 base::Bind(
498 &IsHandledBySafePlugin,
499 GetProfile()->GetResourceContext(),
500 net::FilePathToFileURL(local_path_),
501 mime_type_,
502 RETRY_IF_STALE_PLUGIN_LIST,
503 base::Bind(&DownloadTargetDeterminer::DetermineIfHandledSafelyDone,
504 weak_ptr_factory_.GetWeakPtr())));
505 return QUIT_DOLOOP;
506 #else
507 return CONTINUE;
508 #endif
511 #if defined(ENABLE_PLUGINS)
512 void DownloadTargetDeterminer::DetermineIfHandledSafelyDone(
513 bool is_handled_safely) {
514 DCHECK_CURRENTLY_ON(BrowserThread::UI);
515 DVLOG(20) << "Is file type handled safely: " << is_filetype_handled_safely_;
516 DCHECK_EQ(STATE_DETERMINE_IF_ADOBE_READER_UP_TO_DATE, next_state_);
517 is_filetype_handled_safely_ = is_handled_safely;
518 DoLoop();
520 #endif
522 DownloadTargetDeterminer::Result
523 DownloadTargetDeterminer::DoDetermineIfAdobeReaderUpToDate() {
524 DCHECK_CURRENTLY_ON(BrowserThread::UI);
526 next_state_ = STATE_CHECK_DOWNLOAD_URL;
528 #if defined(OS_WIN)
529 if (!local_path_.MatchesExtension(FILE_PATH_LITERAL(".pdf")))
530 return CONTINUE;
531 if (!IsAdobeReaderDefaultPDFViewer()) {
532 g_is_adobe_reader_up_to_date_ = false;
533 return CONTINUE;
536 base::PostTaskAndReplyWithResult(
537 BrowserThread::GetBlockingPool(),
538 FROM_HERE,
539 base::Bind(&::IsAdobeReaderUpToDate),
540 base::Bind(&DownloadTargetDeterminer::DetermineIfAdobeReaderUpToDateDone,
541 weak_ptr_factory_.GetWeakPtr()));
542 return QUIT_DOLOOP;
543 #else
544 return CONTINUE;
545 #endif
548 #if defined(OS_WIN)
549 void DownloadTargetDeterminer::DetermineIfAdobeReaderUpToDateDone(
550 bool adobe_reader_up_to_date) {
551 DCHECK_CURRENTLY_ON(BrowserThread::UI);
552 DVLOG(20) << "Is Adobe Reader Up To Date: " << adobe_reader_up_to_date;
553 DCHECK_EQ(STATE_CHECK_DOWNLOAD_URL, next_state_);
554 g_is_adobe_reader_up_to_date_ = adobe_reader_up_to_date;
555 DoLoop();
557 #endif
559 DownloadTargetDeterminer::Result
560 DownloadTargetDeterminer::DoCheckDownloadUrl() {
561 DCHECK_CURRENTLY_ON(BrowserThread::UI);
562 DCHECK(!virtual_path_.empty());
563 next_state_ = STATE_CHECK_VISITED_REFERRER_BEFORE;
564 delegate_->CheckDownloadUrl(
565 download_,
566 virtual_path_,
567 base::Bind(&DownloadTargetDeterminer::CheckDownloadUrlDone,
568 weak_ptr_factory_.GetWeakPtr()));
569 return QUIT_DOLOOP;
572 void DownloadTargetDeterminer::CheckDownloadUrlDone(
573 content::DownloadDangerType danger_type) {
574 DCHECK_CURRENTLY_ON(BrowserThread::UI);
575 DVLOG(20) << "URL Check Result:" << danger_type;
576 DCHECK_EQ(STATE_CHECK_VISITED_REFERRER_BEFORE, next_state_);
577 danger_type_ = danger_type;
578 DoLoop();
581 DownloadTargetDeterminer::Result
582 DownloadTargetDeterminer::DoCheckVisitedReferrerBefore() {
583 DCHECK_CURRENTLY_ON(BrowserThread::UI);
585 next_state_ = STATE_DETERMINE_INTERMEDIATE_PATH;
587 // Checking if there are prior visits to the referrer is only necessary if the
588 // danger level of the download depends on the file type.
589 if (danger_type_ != content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS &&
590 danger_type_ != content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT)
591 return CONTINUE;
593 // Assume that:
594 // IsDangerousFile(VISITED_REFERRER) => IsDangerousFile(NO_VISITS_...)
595 // I.e. having visited a referrer only lowers a file's danger level.
596 if (IsDangerousFile(NO_VISITS_TO_REFERRER)) {
597 // Only need to ping the history DB if the download would be considered safe
598 // if there are prior visits and is considered dangerous otherwise.
599 if (!IsDangerousFile(VISITED_REFERRER)) {
600 // HistoryServiceFactory redirects incognito profiles to on-record
601 // profiles. There's no history for on-record profiles in unit_tests.
602 history::HistoryService* history_service =
603 HistoryServiceFactory::GetForProfile(
604 GetProfile(), ServiceAccessType::EXPLICIT_ACCESS);
606 if (history_service && download_->GetReferrerUrl().is_valid()) {
607 history_service->GetVisibleVisitCountToHost(
608 download_->GetReferrerUrl(),
609 base::Bind(
610 &VisitCountsToVisitedBefore,
611 base::Bind(
612 &DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone,
613 weak_ptr_factory_.GetWeakPtr())),
614 &history_tracker_);
615 return QUIT_DOLOOP;
619 // If the danger level doesn't depend on having visited the refererrer URL
620 // or if original profile doesn't have a HistoryService or the referrer url
621 // is invalid, then assume the referrer has not been visited before.
622 is_dangerous_file_ = true;
623 if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)
624 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
626 return CONTINUE;
629 void DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone(
630 bool visited_referrer_before) {
631 DCHECK_CURRENTLY_ON(BrowserThread::UI);
632 DCHECK_EQ(STATE_DETERMINE_INTERMEDIATE_PATH, next_state_);
633 if (IsDangerousFile(visited_referrer_before ? VISITED_REFERRER
634 : NO_VISITS_TO_REFERRER)) {
635 is_dangerous_file_ = true;
636 if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)
637 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
639 DoLoop();
642 DownloadTargetDeterminer::Result
643 DownloadTargetDeterminer::DoDetermineIntermediatePath() {
644 DCHECK_CURRENTLY_ON(BrowserThread::UI);
645 DCHECK(!virtual_path_.empty());
646 DCHECK(!local_path_.empty());
647 DCHECK(intermediate_path_.empty());
648 DCHECK(!virtual_path_.MatchesExtension(kCrdownloadSuffix));
649 DCHECK(!local_path_.MatchesExtension(kCrdownloadSuffix));
651 next_state_ = STATE_NONE;
653 // Note that the intermediate filename is always uniquified (i.e. if a file by
654 // the same name exists, it is never overwritten). Therefore the code below
655 // does not attempt to find a name that doesn't conflict with an existing
656 // file.
658 // If the actual target of the download is a virtual path, then the local path
659 // is considered to point to a temporary path. A separate intermediate path is
660 // unnecessary since the local path already serves that purpose.
661 if (virtual_path_.BaseName() != local_path_.BaseName()) {
662 intermediate_path_ = local_path_;
663 return COMPLETE;
666 // If the download has a forced path and is safe, then just use the
667 // target path. In practice the temporary download file that was created prior
668 // to download filename determination is already named
669 // download_->GetForcedFilePath().
670 if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS &&
671 !download_->GetForcedFilePath().empty()) {
672 DCHECK_EQ(download_->GetForcedFilePath().value(), local_path_.value());
673 intermediate_path_ = local_path_;
674 return COMPLETE;
677 // Other safe downloads get a .crdownload suffix for their intermediate name.
678 if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
679 intermediate_path_ = GetCrDownloadPath(local_path_);
680 return COMPLETE;
683 // If this is a resumed download, then re-use the existing intermediate path
684 // if one is available. A resumed download shouldn't cause a non-dangerous
685 // download to be considered dangerous upon resumption. Therefore the
686 // intermediate file should already be in the correct form.
687 if (is_resumption_ && !download_->GetFullPath().empty() &&
688 local_path_.DirName() == download_->GetFullPath().DirName()) {
689 DCHECK_NE(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
690 download_->GetDangerType());
691 DCHECK_EQ(kCrdownloadSuffix, download_->GetFullPath().Extension());
692 intermediate_path_ = download_->GetFullPath();
693 return COMPLETE;
696 // Dangerous downloads receive a random intermediate name that looks like:
697 // 'Unconfirmed <random>.crdownload'.
698 const base::FilePath::CharType kUnconfirmedFormatSuffix[] =
699 FILE_PATH_LITERAL(" %d.crdownload");
700 // Range of the <random> uniquifier.
701 const int kUnconfirmedUniquifierRange = 1000000;
702 #if defined(OS_WIN)
703 base::string16 unconfirmed_format =
704 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
705 #else
706 std::string unconfirmed_format =
707 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
708 #endif
709 unconfirmed_format.append(kUnconfirmedFormatSuffix);
711 base::FilePath::StringType file_name = base::StringPrintf(
712 unconfirmed_format.c_str(),
713 base::RandInt(0, kUnconfirmedUniquifierRange));
714 intermediate_path_ = local_path_.DirName().Append(file_name);
715 return COMPLETE;
718 void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() {
719 DCHECK(download_);
720 DVLOG(20) << "Scheduling callback. Virtual:" << virtual_path_.AsUTF8Unsafe()
721 << " Local:" << local_path_.AsUTF8Unsafe()
722 << " Intermediate:" << intermediate_path_.AsUTF8Unsafe()
723 << " Should prompt:" << should_prompt_
724 << " Danger type:" << danger_type_
725 << " Is dangerous file:" << is_dangerous_file_;
726 scoped_ptr<DownloadTargetInfo> target_info(new DownloadTargetInfo);
728 target_info->target_path = local_path_;
729 target_info->target_disposition =
730 (HasPromptedForPath() || should_prompt_
731 ? DownloadItem::TARGET_DISPOSITION_PROMPT
732 : DownloadItem::TARGET_DISPOSITION_OVERWRITE);
733 target_info->danger_type = danger_type_;
734 target_info->is_dangerous_file = is_dangerous_file_;
735 target_info->intermediate_path = intermediate_path_;
736 target_info->mime_type = mime_type_;
737 target_info->is_filetype_handled_safely = is_filetype_handled_safely_;
739 base::ThreadTaskRunnerHandle::Get()->PostTask(
740 FROM_HERE, base::Bind(completion_callback_, base::Passed(&target_info)));
741 completion_callback_.Reset();
742 delete this;
745 void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() {
746 // Path substitution failed.
747 virtual_path_.clear();
748 local_path_.clear();
749 intermediate_path_.clear();
750 ScheduleCallbackAndDeleteSelf();
753 Profile* DownloadTargetDeterminer::GetProfile() {
754 DCHECK(download_->GetBrowserContext());
755 return Profile::FromBrowserContext(download_->GetBrowserContext());
758 bool DownloadTargetDeterminer::ShouldPromptForDownload(
759 const base::FilePath& filename) const {
760 if (is_resumption_) {
761 // For resumed downloads, if the target disposition or prefs require
762 // prompting, the user has already been prompted. Try to respect the user's
763 // selection, unless we've discovered that the target path cannot be used
764 // for some reason.
765 content::DownloadInterruptReason reason = download_->GetLastReason();
766 return (reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED ||
767 reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE ||
768 reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE);
771 // If the download path is forced, don't prompt.
772 if (!download_->GetForcedFilePath().empty()) {
773 // 'Save As' downloads shouldn't have a forced path.
774 DCHECK(DownloadItem::TARGET_DISPOSITION_PROMPT !=
775 download_->GetTargetDisposition());
776 return false;
779 // Don't ask where to save if the download path is managed. Even if the user
780 // wanted to be prompted for "all" downloads, or if this was a 'Save As'
781 // download.
782 if (download_prefs_->IsDownloadPathManaged())
783 return false;
785 // Prompt if this is a 'Save As' download.
786 if (download_->GetTargetDisposition() ==
787 DownloadItem::TARGET_DISPOSITION_PROMPT)
788 return true;
790 // Check if the user has the "Always prompt for download location" preference
791 // set. If so we prompt for most downloads except for the following scenarios:
792 // 1) Extension installation. Note that we only care here about the case where
793 // an extension is installed, not when one is downloaded with "save as...".
794 // 2) Filetypes marked "always open." If the user just wants this file opened,
795 // don't bother asking where to keep it.
796 if (download_prefs_->PromptForDownload() &&
797 !download_crx_util::IsExtensionDownload(*download_) &&
798 !filename.MatchesExtension(extensions::kExtensionFileExtension) &&
799 !download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename))
800 return true;
802 // Otherwise, don't prompt. Note that the user might still be prompted if
803 // there are unresolved conflicts during path reservation (e.g. due to the
804 // target path being unwriteable or because there are too many conflicting
805 // files), or if an extension signals that the user be prompted on a filename
806 // conflict.
807 return false;
810 bool DownloadTargetDeterminer::HasPromptedForPath() const {
811 return (is_resumption_ && download_->GetTargetDisposition() ==
812 DownloadItem::TARGET_DISPOSITION_PROMPT);
815 bool DownloadTargetDeterminer::IsDangerousFile(PriorVisitsToReferrer visits) {
816 DCHECK_CURRENTLY_ON(BrowserThread::UI);
818 // If the user has has been prompted or will be, assume that the user has
819 // approved the download. A programmatic download is considered safe unless it
820 // contains malware.
821 if (HasPromptedForPath() || should_prompt_ ||
822 !download_->GetForcedFilePath().empty())
823 return false;
825 const bool is_extension_download =
826 download_crx_util::IsExtensionDownload(*download_);
828 // User-initiated extension downloads from pref-whitelisted sources are not
829 // considered dangerous.
830 if (download_->HasUserGesture() &&
831 is_extension_download &&
832 download_crx_util::OffStoreInstallAllowedByPrefs(
833 GetProfile(), *download_)) {
834 return false;
837 #if defined(ENABLE_EXTENSIONS)
838 // Extensions that are not from the gallery are considered dangerous.
839 // When off-store install is disabled we skip this, since in this case, we
840 // will not offer to install the extension.
841 if (extensions::FeatureSwitch::easy_off_store_install()->IsEnabled() &&
842 is_extension_download &&
843 !extensions::WebstoreInstaller::GetAssociatedApproval(*download_)) {
844 return true;
846 #endif
848 // Anything the user has marked auto-open is OK if it's user-initiated.
849 if (download_prefs_->IsAutoOpenEnabledBasedOnExtension(virtual_path_) &&
850 download_->HasUserGesture())
851 return false;
853 switch (download_util::GetFileDangerLevel(virtual_path_.BaseName())) {
854 case download_util::NOT_DANGEROUS:
855 return false;
857 case download_util::ALLOW_ON_USER_GESTURE:
858 // "Allow on user gesture" is OK when we have a user gesture and the
859 // hosting page has been visited before today.
860 if (download_->GetTransitionType() &
861 ui::PAGE_TRANSITION_FROM_ADDRESS_BAR) {
862 return false;
864 return !download_->HasUserGesture() || visits == NO_VISITS_TO_REFERRER;
866 case download_util::DANGEROUS:
867 return true;
869 NOTREACHED();
870 return false;
873 void DownloadTargetDeterminer::OnDownloadDestroyed(
874 DownloadItem* download) {
875 DCHECK_CURRENTLY_ON(BrowserThread::UI);
876 DCHECK_EQ(download_, download);
877 CancelOnFailureAndDeleteSelf();
880 // static
881 void DownloadTargetDeterminer::Start(content::DownloadItem* download,
882 const base::FilePath& initial_virtual_path,
883 DownloadPrefs* download_prefs,
884 DownloadTargetDeterminerDelegate* delegate,
885 const CompletionCallback& callback) {
886 // DownloadTargetDeterminer owns itself and will self destruct when the job is
887 // complete or the download item is destroyed. The callback is always invoked
888 // asynchronously.
889 new DownloadTargetDeterminer(download, initial_virtual_path, download_prefs,
890 delegate, callback);
893 // static
894 base::FilePath DownloadTargetDeterminer::GetCrDownloadPath(
895 const base::FilePath& suggested_path) {
896 return base::FilePath(suggested_path.value() + kCrdownloadSuffix);
899 #if defined(OS_WIN)
900 // static
901 bool DownloadTargetDeterminer::IsAdobeReaderUpToDate() {
902 return g_is_adobe_reader_up_to_date_;
904 #endif