Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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. | |
| 4 | |
| 5 #include "chrome/browser/download/download_target_determiner.h" | |
| 6 | |
| 7 #include "base/prefs/pref_service.h" | |
| 8 #include "base/rand_util.h" | |
| 9 #include "base/stringprintf.h" | |
| 10 #include "base/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/download/download_util.h" | |
| 16 #include "chrome/browser/extensions/api/downloads/downloads_api.h" | |
| 17 #include "chrome/browser/extensions/webstore_installer.h" | |
| 18 #include "chrome/browser/history/history_service.h" | |
| 19 #include "chrome/browser/history/history_service_factory.h" | |
| 20 #include "chrome/browser/profiles/profile.h" | |
| 21 #include "chrome/browser/safe_browsing/download_protection_service.h" | |
| 22 #include "chrome/common/extensions/feature_switch.h" | |
| 23 #include "chrome/common/pref_names.h" | |
| 24 #include "content/public/browser/browser_context.h" | |
| 25 #include "content/public/browser/browser_thread.h" | |
| 26 #include "grit/generated_resources.h" | |
| 27 #include "net/base/net_util.h" | |
| 28 #include "ui/base/l10n/l10n_util.h" | |
| 29 | |
| 30 #if defined(OS_CHROMEOS) | |
| 31 #include "chrome/browser/chromeos/drive/drive_download_handler.h" | |
| 32 #include "chrome/browser/chromeos/drive/drive_file_system_util.h" | |
| 33 #endif | |
| 34 | |
| 35 using content::BrowserThread; | |
| 36 using content::DownloadItem; | |
| 37 using safe_browsing::DownloadProtectionService; | |
| 38 | |
| 39 namespace { | |
| 40 | |
| 41 // Condenses the results from HistoryService::GetVisibleVisitCountToHost() to a | |
| 42 // single bool. A host is considered visited before if prior visible vists were | |
| 43 // found in history and the first such visit was earlier than the most recent | |
| 44 // midnight. | |
| 45 void VisitCountsToVisitedBefore( | |
| 46 const base::Callback<void(bool)>& callback, | |
| 47 HistoryService::Handle unused_handle, | |
| 48 bool found_visits, | |
| 49 int count, | |
| 50 base::Time first_visit) { | |
| 51 callback.Run( | |
| 52 found_visits && count > 0 && | |
| 53 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight())); | |
| 54 } | |
| 55 | |
| 56 } // namespace | |
| 57 | |
| 58 DownloadTargetDeterminerDelegate::~DownloadTargetDeterminerDelegate() { | |
| 59 } | |
| 60 | |
| 61 DownloadTargetDeterminer::DownloadTargetDeterminer( | |
| 62 DownloadItem* download, | |
| 63 DownloadPrefs* download_prefs, | |
| 64 const base::FilePath& last_selected_directory, | |
| 65 DownloadTargetDeterminerDelegate* delegate, | |
| 66 const CompletionCallback& callback) | |
| 67 : next_state_(STATE_GENERATE_TARGET_PATH), | |
| 68 should_prompt_(false), | |
| 69 should_overwrite_(!download->GetForcedFilePath().empty()), | |
| 70 danger_type_(download->GetDangerType()), | |
| 71 download_(download), | |
| 72 download_prefs_(download_prefs), | |
| 73 delegate_(delegate), | |
| 74 last_selected_directory_(last_selected_directory), | |
| 75 completion_callback_(callback), | |
| 76 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
| 77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 78 DCHECK(download_); | |
| 79 DCHECK(delegate); | |
| 80 download_->AddObserver(this); | |
| 81 | |
| 82 DoLoop(); | |
| 83 } | |
| 84 | |
| 85 DownloadTargetDeterminer::~DownloadTargetDeterminer() { | |
| 86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 87 DCHECK(download_); | |
| 88 DCHECK(completion_callback_.is_null()); | |
| 89 download_->RemoveObserver(this); | |
| 90 } | |
| 91 | |
| 92 void DownloadTargetDeterminer::DoLoop() { | |
| 93 Result result = CONTINUE; | |
| 94 | |
| 95 do { | |
| 96 switch (next_state_) { | |
| 97 case STATE_GENERATE_TARGET_PATH: | |
| 98 result = DoGenerateTargetPath(); | |
| 99 break; | |
| 100 case STATE_NOTIFY_EXTENSIONS: | |
| 101 result = DoNotifyExtensions(); | |
| 102 break; | |
| 103 case STATE_RESERVE_VIRTUAL_PATH: | |
| 104 result = DoReserveVirtualPath(); | |
| 105 break; | |
| 106 case STATE_PROMPT_USER_FOR_DOWNLOAD_PATH: | |
| 107 result = DoPromptUserForDownloadPath(); | |
| 108 break; | |
| 109 case STATE_DETERMINE_LOCAL_PATH: | |
| 110 result = DoDetermineLocalPath(); | |
| 111 break; | |
| 112 case STATE_CHECK_DOWNLOAD_URL: | |
| 113 result = DoCheckDownloadUrl(); | |
| 114 break; | |
| 115 case STATE_DETERMINE_DANGER_TYPE: | |
| 116 result = DoDetermineDangerType(); | |
| 117 break; | |
| 118 case STATE_DETERMINE_INTERMEDIATE_PATH: | |
| 119 result = DoDetermineIntermediatePath(); | |
| 120 break; | |
| 121 case STATE_CHECK_VISITED_REFERRER_BEFORE: | |
| 122 result = DoCheckVisitedReferrerBefore(); | |
| 123 break; | |
| 124 case STATE_NONE: | |
| 125 NOTREACHED(); | |
| 126 break; | |
| 127 } | |
| 128 } while (result == CONTINUE && next_state_ != STATE_NONE); | |
| 129 | |
| 130 if (result == COMPLETE) | |
| 131 ScheduleCallbackAndDeleteSelf(); | |
| 132 } | |
| 133 | |
| 134 DownloadTargetDeterminer::Result | |
| 135 DownloadTargetDeterminer::DoGenerateTargetPath() { | |
| 136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 137 DCHECK(virtual_path_.empty()); | |
| 138 DCHECK(local_path_.empty()); | |
| 139 bool is_forced_path = !download_->GetForcedFilePath().empty(); | |
| 140 base::FilePath suggested_path; | |
| 141 | |
| 142 next_state_ = STATE_NOTIFY_EXTENSIONS; | |
| 143 | |
| 144 // If we don't have a forced path, we should construct a path for the | |
| 145 // download. Forced paths are only specified for programmatic downloads | |
| 146 // (WebStore, Drag&Drop). | |
| 147 if (!is_forced_path) { | |
| 148 std::string default_filename( | |
| 149 l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME)); | |
| 150 base::FilePath generated_filename = net::GenerateFileName( | |
| 151 download_->GetURL(), | |
| 152 download_->GetContentDisposition(), | |
| 153 GetProfile()->GetPrefs()->GetString(prefs::kDefaultCharset), | |
| 154 download_->GetSuggestedFilename(), | |
| 155 download_->GetMimeType(), | |
| 156 default_filename); | |
| 157 should_prompt_ = ShouldPromptForDownload(generated_filename); | |
| 158 base::FilePath target_directory; | |
| 159 if (should_prompt_ && !last_selected_directory_.empty()) { | |
| 160 DCHECK(!download_prefs_->IsDownloadPathManaged()); | |
| 161 // If the user is going to be prompted and the user has been prompted | |
| 162 // before, then always prefer the last directory that the user selected. | |
| 163 target_directory = last_selected_directory_; | |
| 164 } else { | |
| 165 target_directory = download_prefs_->DownloadPath(); | |
| 166 } | |
| 167 suggested_path = target_directory.Append(generated_filename); | |
| 168 } else { | |
| 169 DCHECK(!should_prompt_); | |
| 170 suggested_path = download_->GetForcedFilePath(); | |
| 171 } | |
| 172 DCHECK(suggested_path.IsAbsolute()); | |
| 173 | |
| 174 // Treat the suggested_path as a virtual path. We will eventually determine | |
| 175 // whether this is a local path and if not, figure out a local path. | |
| 176 virtual_path_ = suggested_path; | |
|
benjhayden
2013/04/09 15:46:32
Why not assign directly to virtual_path_ above ins
asanka
2013/04/16 20:34:01
Done.
| |
| 177 DVLOG(20) << "Generated virtual path: " << virtual_path_.AsUTF8Unsafe(); | |
| 178 | |
| 179 // If the download is DOA, don't bother going any further. This would be the | |
| 180 // case for a download that failed to initialize (e.g. the initial temporary | |
| 181 // file couldn't be created because both the downloads directory and the | |
| 182 // temporary directory are unwriteable). | |
| 183 if (!download_->IsInProgress()) | |
| 184 return COMPLETE; | |
| 185 return CONTINUE; | |
| 186 } | |
| 187 | |
| 188 DownloadTargetDeterminer::Result | |
| 189 DownloadTargetDeterminer::DoNotifyExtensions() { | |
| 190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 191 DCHECK(!virtual_path_.empty()); | |
| 192 ExtensionDownloadsEventRouter* router = delegate_->GetExtensionEventRouter(); | |
| 193 | |
| 194 next_state_ = STATE_RESERVE_VIRTUAL_PATH; | |
| 195 | |
| 196 // If the target path is forced or if we don't have an extensions event | |
| 197 // router, then proceed with the original path. | |
| 198 if (!download_->GetForcedFilePath().empty() || !router) | |
| 199 return CONTINUE; | |
| 200 | |
| 201 base::Closure original_path_callback = | |
| 202 base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone, | |
| 203 weak_ptr_factory_.GetWeakPtr(), base::FilePath(), false); | |
| 204 ExtensionDownloadsEventRouter::FilenameChangedCallback override_callback = | |
| 205 base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone, | |
| 206 weak_ptr_factory_.GetWeakPtr()); | |
| 207 router->OnDeterminingFilename(download_, virtual_path_.BaseName(), | |
| 208 original_path_callback, | |
| 209 override_callback); | |
| 210 return PENDING; | |
| 211 } | |
| 212 | |
| 213 void DownloadTargetDeterminer::NotifyExtensionsDone( | |
| 214 const base::FilePath& suggested_path, | |
| 215 bool should_overwrite) { | |
| 216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 217 DVLOG(20) << "Extension suggested path: " << suggested_path.AsUTF8Unsafe(); | |
| 218 | |
| 219 if (!suggested_path.empty()) { | |
| 220 // TODO(asanka, benjhayden): The suggested path may contain path fragments. | |
| 221 // We need to validate each component individually. http://crbug.com/181332. | |
| 222 | |
| 223 // If an extension overrides the filename, then the target directory will be | |
| 224 // forced to download_prefs_->DownloadPath() since extensions cannot place | |
| 225 // downloaded files anywhere except there. This prevents subdirectories from | |
| 226 // accumulating: if an extension is allowed to say that a file should go in | |
| 227 // last_download_path/music/foo.mp3, then last_download_path will accumulate | |
| 228 // the subdirectory /music/ so that the next download may end up in | |
| 229 // Downloads/music/music/music/bar.mp3. | |
| 230 base::FilePath new_path(download_prefs_->DownloadPath().Append( | |
| 231 suggested_path).NormalizePathSeparators()); | |
| 232 // Do not pass a mime type to GenerateSafeFileName so that it does not force | |
| 233 // the filename to have an extension if the (Chrome) extension does not | |
| 234 // suggest it. | |
| 235 net::GenerateSafeFileName(std::string(), false, &new_path); | |
| 236 virtual_path_ = new_path; | |
| 237 | |
| 238 // If |is_forced_path| were true, then extensions would not have been | |
| 239 // consulted, so use |overwrite| instead of |is_forced_path|. This does NOT | |
| 240 // set DownloadItem::GetForcedFilePath()! | |
| 241 should_overwrite_ = should_overwrite; | |
| 242 } | |
| 243 | |
| 244 DoLoop(); | |
| 245 } | |
| 246 | |
| 247 DownloadTargetDeterminer::Result | |
| 248 DownloadTargetDeterminer::DoReserveVirtualPath() { | |
| 249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 250 DCHECK(!virtual_path_.empty()); | |
| 251 | |
| 252 next_state_ = STATE_PROMPT_USER_FOR_DOWNLOAD_PATH; | |
| 253 | |
| 254 delegate_->ReserveVirtualPath( | |
| 255 download_, virtual_path_, !should_overwrite_, | |
| 256 base::Bind(&DownloadTargetDeterminer::ReserveVirtualPathDone, | |
| 257 weak_ptr_factory_.GetWeakPtr())); | |
| 258 return PENDING; | |
| 259 } | |
| 260 | |
| 261 void DownloadTargetDeterminer::ReserveVirtualPathDone( | |
| 262 const base::FilePath& path, bool verified) { | |
| 263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 264 DVLOG(20) << "Reserved path: " << path.AsUTF8Unsafe() | |
| 265 << " Verified:" << verified; | |
| 266 should_prompt_ = (should_prompt_ || !verified); | |
| 267 if (verified) | |
| 268 virtual_path_ = path; | |
| 269 DoLoop(); | |
| 270 } | |
| 271 | |
| 272 DownloadTargetDeterminer::Result | |
| 273 DownloadTargetDeterminer::DoPromptUserForDownloadPath() { | |
| 274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 275 DCHECK(!virtual_path_.empty()); | |
| 276 | |
| 277 // We also prompt if the download path couldn't be verified. | |
| 278 if (should_prompt_) { | |
| 279 // If we are prompting, then delegate_->PromptUserForDownloadPathDone() | |
| 280 // returns both a local and virtual path, so we don't need to determine a | |
| 281 // local path in that case. | |
| 282 next_state_ = STATE_CHECK_DOWNLOAD_URL; | |
| 283 delegate_->PromptUserForDownloadPath( | |
| 284 download_, | |
| 285 virtual_path_, | |
| 286 base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone, | |
| 287 weak_ptr_factory_.GetWeakPtr())); | |
| 288 return PENDING; | |
| 289 } | |
| 290 | |
| 291 next_state_ = STATE_DETERMINE_LOCAL_PATH; | |
| 292 return CONTINUE; | |
| 293 } | |
| 294 | |
| 295 void DownloadTargetDeterminer::PromptUserForDownloadPathDone( | |
| 296 const base::FilePath& virtual_path, | |
| 297 const base::FilePath& local_path) { | |
| 298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 299 DVLOG(20) << "User selected paths (virtual):" << virtual_path.AsUTF8Unsafe() | |
| 300 << " (local):" << local_path.AsUTF8Unsafe(); | |
| 301 if (virtual_path.empty()) { | |
| 302 CancelOnFailureAndDeleteSelf(); | |
| 303 return; | |
| 304 } | |
| 305 virtual_path_ = virtual_path; | |
| 306 local_path_ = local_path; | |
| 307 | |
| 308 DCHECK_EQ(STATE_CHECK_DOWNLOAD_URL, next_state_); | |
| 309 DoLoop(); | |
| 310 } | |
| 311 | |
| 312 DownloadTargetDeterminer::Result | |
| 313 DownloadTargetDeterminer::DoDetermineLocalPath() { | |
| 314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 315 DCHECK(!virtual_path_.empty()); | |
| 316 DCHECK(local_path_.empty()); | |
| 317 | |
| 318 next_state_ = STATE_CHECK_DOWNLOAD_URL; | |
| 319 | |
| 320 delegate_->DetermineLocalPath( | |
| 321 download_, | |
| 322 virtual_path_, | |
| 323 base::Bind(&DownloadTargetDeterminer::DetermineLocalPathDone, | |
| 324 weak_ptr_factory_.GetWeakPtr())); | |
| 325 return PENDING; | |
| 326 } | |
| 327 | |
| 328 void DownloadTargetDeterminer::DetermineLocalPathDone( | |
| 329 const base::FilePath& local_path) { | |
| 330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 331 DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe(); | |
| 332 local_path_ = local_path; | |
| 333 if (local_path_.empty()) { | |
| 334 // Path subsitution failed. | |
| 335 CancelOnFailureAndDeleteSelf(); | |
| 336 return; | |
| 337 } | |
| 338 DoLoop(); | |
| 339 } | |
| 340 | |
| 341 DownloadTargetDeterminer::Result | |
| 342 DownloadTargetDeterminer::DoCheckDownloadUrl() { | |
| 343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 344 | |
| 345 next_state_ = STATE_DETERMINE_DANGER_TYPE; | |
| 346 | |
| 347 #if defined(FULL_SAFE_BROWSING) | |
| 348 safe_browsing::DownloadProtectionService* service = | |
| 349 delegate_->GetDownloadProtectionService(); | |
| 350 if (service) { | |
| 351 VLOG(2) << __FUNCTION__ << "() Start SB URL check for download = " | |
| 352 << download_->DebugString(false); | |
| 353 service->CheckDownloadUrl( | |
| 354 *download_, | |
| 355 base::Bind( | |
| 356 &DownloadTargetDeterminer::CheckDownloadUrlDone, | |
| 357 weak_ptr_factory_.GetWeakPtr())); | |
| 358 return PENDING; | |
| 359 } | |
| 360 #endif | |
| 361 return CONTINUE; | |
| 362 } | |
| 363 | |
| 364 void DownloadTargetDeterminer::CheckDownloadUrlDone( | |
| 365 DownloadProtectionService::DownloadCheckResult result) { | |
| 366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 367 DVLOG(20) << "URL Check Result:" << result; | |
| 368 if (result != DownloadProtectionService::SAFE) | |
| 369 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL; | |
| 370 DoLoop(); | |
| 371 } | |
| 372 | |
| 373 DownloadTargetDeterminer::Result | |
| 374 DownloadTargetDeterminer::DoDetermineDangerType() { | |
| 375 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 376 DCHECK(!virtual_path_.empty()); | |
| 377 | |
| 378 next_state_ = STATE_DETERMINE_INTERMEDIATE_PATH; | |
| 379 | |
| 380 #if defined(FULL_SAFE_BROWSING) | |
| 381 // If the download hasn't already been marked dangerous (could be | |
| 382 // DANGEROUS_URL), check if it is a dangerous file. | |
| 383 if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) { | |
| 384 // If this type of files is handled by the enhanced SafeBrowsing download | |
| 385 // protection, mark it as potentially dangerous content until we are done | |
| 386 // with scanning it. | |
| 387 safe_browsing::DownloadProtectionService* service = | |
| 388 delegate_->GetDownloadProtectionService(); | |
| 389 if (service && service->IsSupportedDownload(*download_, virtual_path_)) { | |
| 390 // TODO(noelutz): if the user changes the extension name in the UI to | |
| 391 // something like .exe SafeBrowsing will currently *not* check if the | |
| 392 // download is malicious. | |
| 393 danger_type_ = content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT; | |
| 394 } | |
| 395 } else { | |
| 396 DCHECK_EQ(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, danger_type_); | |
| 397 } | |
| 398 #endif | |
| 399 | |
| 400 if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS && | |
| 401 !should_prompt_ && | |
| 402 download_->GetForcedFilePath().empty()) { | |
| 403 next_state_ = STATE_CHECK_VISITED_REFERRER_BEFORE; | |
| 404 } | |
| 405 return CONTINUE; | |
| 406 } | |
| 407 | |
| 408 DownloadTargetDeterminer::Result | |
| 409 DownloadTargetDeterminer::DoCheckVisitedReferrerBefore() { | |
| 410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 411 | |
| 412 next_state_ = STATE_DETERMINE_INTERMEDIATE_PATH; | |
| 413 | |
| 414 // HistoryServiceFactory redirects incognito profiles to on-record profiles. | |
| 415 HistoryService* history_service = HistoryServiceFactory::GetForProfile( | |
| 416 GetProfile(), Profile::EXPLICIT_ACCESS); | |
| 417 | |
| 418 if (history_service && download_->GetReferrerUrl().is_valid()) { | |
| 419 history_service->GetVisibleVisitCountToHost( | |
| 420 download_->GetReferrerUrl(), &history_consumer_, | |
| 421 base::Bind(&VisitCountsToVisitedBefore, base::Bind( | |
| 422 &DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone, | |
| 423 weak_ptr_factory_.GetWeakPtr()))); | |
| 424 return PENDING; | |
| 425 } | |
| 426 | |
| 427 // If the original profile doesn't have a HistoryService or the referrer url | |
| 428 // is invalid, then give up and assume the referrer has not been visited | |
| 429 // before. There's no history for on-record profiles in unit_tests, for | |
| 430 // example. | |
| 431 if (IsDangerousFile(false)) | |
| 432 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE; | |
| 433 return CONTINUE; | |
| 434 } | |
| 435 | |
| 436 void DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone( | |
| 437 bool visited_referrer_before) { | |
| 438 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 439 if (IsDangerousFile(visited_referrer_before)) | |
| 440 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE; | |
| 441 DoLoop(); | |
| 442 } | |
| 443 | |
| 444 DownloadTargetDeterminer::Result | |
| 445 DownloadTargetDeterminer::DoDetermineIntermediatePath() { | |
| 446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 447 DCHECK(!virtual_path_.empty()); | |
| 448 DCHECK(!local_path_.empty()); | |
| 449 DCHECK(intermediate_path_.empty()); | |
| 450 | |
| 451 next_state_ = STATE_NONE; | |
| 452 | |
| 453 if (virtual_path_.BaseName() != local_path_.BaseName() || | |
| 454 (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS && | |
| 455 !download_->GetForcedFilePath().empty())) { | |
| 456 // If the actual target of the download is a virtual path, then the local | |
| 457 // path is considered to point to a temporary path. A separate intermediate | |
| 458 // path is unnecessary since the local path already serves that purpose. | |
| 459 // | |
| 460 // Also, if the download has a forced path and is safe, then just use the | |
| 461 // target path. | |
| 462 intermediate_path_ = local_path_; | |
| 463 } else if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) { | |
| 464 // If the download is not dangerous, just append .crdownload to the target | |
| 465 // path. | |
| 466 intermediate_path_ = download_util::GetCrDownloadPath(local_path_); | |
| 467 } else { | |
| 468 // If the download is potentially dangerous we create a filename of the form | |
| 469 // 'Unconfirmed <random>.crdownload'. | |
| 470 base::FilePath::StringType file_name; | |
| 471 base::FilePath dir = local_path_.DirName(); | |
| 472 #if defined(OS_WIN) | |
| 473 string16 unconfirmed_prefix = | |
| 474 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); | |
| 475 #else | |
| 476 std::string unconfirmed_prefix = | |
| 477 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); | |
| 478 #endif | |
| 479 base::SStringPrintf( | |
| 480 &file_name, | |
| 481 unconfirmed_prefix.append( | |
| 482 FILE_PATH_LITERAL(" %d.crdownload")).c_str(), | |
| 483 base::RandInt(0, 1000000)); | |
| 484 intermediate_path_ = dir.Append(file_name); | |
| 485 } | |
| 486 | |
| 487 return COMPLETE; | |
| 488 } | |
| 489 | |
| 490 void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() { | |
| 491 DCHECK(download_); | |
| 492 DVLOG(20) << "Scheduling callback. Virtual:" << virtual_path_.AsUTF8Unsafe() | |
| 493 << " Local:" << local_path_.AsUTF8Unsafe() | |
| 494 << " Intermediate:" << intermediate_path_.AsUTF8Unsafe() | |
| 495 << " Should prompt:" << should_prompt_ | |
| 496 << " Danger type:" << danger_type_; | |
| 497 MessageLoop::current()->PostTask( | |
| 498 FROM_HERE, | |
| 499 base::Bind(completion_callback_, | |
| 500 virtual_path_, | |
| 501 local_path_, | |
| 502 intermediate_path_, | |
| 503 (should_prompt_ ? DownloadItem::TARGET_DISPOSITION_PROMPT : | |
| 504 DownloadItem::TARGET_DISPOSITION_OVERWRITE), | |
| 505 danger_type_)); | |
| 506 completion_callback_.Reset(); | |
| 507 delete this; | |
| 508 } | |
| 509 | |
| 510 void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() { | |
| 511 // Path substitution failed. | |
| 512 virtual_path_.clear(); | |
| 513 local_path_.clear(); | |
| 514 intermediate_path_.clear(); | |
| 515 ScheduleCallbackAndDeleteSelf(); | |
| 516 } | |
| 517 | |
| 518 Profile* DownloadTargetDeterminer::GetProfile() { | |
| 519 DCHECK(download_->GetBrowserContext()); | |
| 520 return Profile::FromBrowserContext(download_->GetBrowserContext()); | |
| 521 } | |
| 522 | |
| 523 bool DownloadTargetDeterminer::ShouldPromptForDownload( | |
| 524 const base::FilePath& filename) { | |
| 525 // If the download path is forced, don't prompt. | |
| 526 if (!download_->GetForcedFilePath().empty()) { | |
| 527 // 'Save As' downloads shouldn't have a forced path. | |
| 528 DCHECK_NE(DownloadItem::TARGET_DISPOSITION_PROMPT, | |
| 529 download_->GetTargetDisposition()); | |
| 530 return false; | |
| 531 } | |
| 532 | |
| 533 // Don't ask where to save if the download path is managed. Even if the user | |
| 534 // wanted to be prompted for "all" downloads, or if this was a 'Save As' | |
|
benjhayden
2013/04/09 15:46:32
What if the user wants to change the basename?
asanka
2013/04/16 20:34:01
I don't intend to change the semantics of managed
| |
| 535 // download. | |
| 536 if (download_prefs_->IsDownloadPathManaged()) | |
| 537 return false; | |
| 538 | |
| 539 // Prompt if this is a 'Save As' download. | |
| 540 if (download_->GetTargetDisposition() == | |
| 541 DownloadItem::TARGET_DISPOSITION_PROMPT) | |
| 542 return true; | |
| 543 | |
| 544 // Check if the user has the "Always prompt for download location" preference | |
| 545 // set. If so we prompt for most downloads except for the following scenarios: | |
| 546 // 1) Extension installation. Note that we only care here about the case where | |
| 547 // an extension is installed, not when one is downloaded with "save as...". | |
|
benjhayden
2013/04/09 15:46:32
Won't L552 prevent prompting if a user Saves As a
asanka
2013/04/16 20:34:01
Save As downloads have TARGET_DISPOSITION_PROMPT s
| |
| 548 // 2) Filetypes marked "always open." If the user just wants this file opened, | |
| 549 // don't bother asking where to keep it. | |
| 550 if (download_prefs_->PromptForDownload() && | |
| 551 !download_crx_util::IsExtensionDownload(*download_) && | |
| 552 !extensions::Extension::IsExtension(filename) && | |
| 553 !download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename)) | |
| 554 return true; | |
| 555 | |
| 556 // Otherwise, don't prompt. | |
| 557 return false; | |
| 558 } | |
| 559 | |
| 560 bool DownloadTargetDeterminer::IsDangerousFile(bool visited_referrer_before) { | |
| 561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 562 const bool is_extension_download = | |
| 563 download_crx_util::IsExtensionDownload(*download_); | |
| 564 | |
| 565 // User-initiated extension downloads from pref-whitelisted sources are not | |
| 566 // considered dangerous. | |
| 567 if (download_->HasUserGesture() && | |
| 568 is_extension_download && | |
| 569 download_crx_util::OffStoreInstallAllowedByPrefs( | |
| 570 GetProfile(), *download_)) { | |
| 571 return false; | |
| 572 } | |
| 573 | |
| 574 // Extensions that are not from the gallery are considered dangerous. | |
| 575 // When off-store install is disabled we skip this, since in this case, we | |
| 576 // will not offer to install the extension. | |
| 577 if (extensions::FeatureSwitch::easy_off_store_install()->IsEnabled() && | |
| 578 is_extension_download && | |
| 579 !extensions::WebstoreInstaller::GetAssociatedApproval(*download_)) { | |
| 580 return true; | |
| 581 } | |
| 582 | |
| 583 // Anything the user has marked auto-open is OK if it's user-initiated. | |
| 584 if (download_prefs_->IsAutoOpenEnabledBasedOnExtension(virtual_path_) && | |
| 585 download_->HasUserGesture()) | |
| 586 return false; | |
| 587 | |
| 588 switch (download_util::GetFileDangerLevel(virtual_path_.BaseName())) { | |
| 589 case download_util::NotDangerous: | |
| 590 return false; | |
| 591 | |
| 592 case download_util::AllowOnUserGesture: | |
| 593 // "Allow on user gesture" is OK when we have a user gesture and the | |
| 594 // hosting page has been visited before today. | |
| 595 if (download_->GetTransitionType() & | |
| 596 content::PAGE_TRANSITION_FROM_ADDRESS_BAR) { | |
| 597 return false; | |
| 598 } | |
| 599 return !download_->HasUserGesture() || !visited_referrer_before; | |
| 600 | |
| 601 case download_util::Dangerous: | |
| 602 return true; | |
| 603 } | |
| 604 NOTREACHED(); | |
| 605 return false; | |
| 606 } | |
| 607 | |
| 608 void DownloadTargetDeterminer::OnDownloadDestroyed( | |
| 609 DownloadItem* download) { | |
| 610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 611 DCHECK_EQ(download_, download); | |
| 612 CancelOnFailureAndDeleteSelf(); | |
| 613 } | |
| 614 | |
| 615 // static | |
| 616 void DownloadTargetDeterminer::Start( | |
| 617 content::DownloadItem* download, | |
| 618 DownloadPrefs* download_prefs, | |
| 619 const base::FilePath& last_selected_directory, | |
| 620 DownloadTargetDeterminerDelegate* delegate, | |
| 621 const CompletionCallback& callback) { | |
| 622 // DownloadTargetDeterminer owns itself and will self destruct when the job is | |
| 623 // complete or the download item is destroyed. The callback is always invoked | |
| 624 // asynchronously. | |
| 625 new DownloadTargetDeterminer(download, download_prefs, | |
| 626 last_selected_directory, delegate, callback); | |
| 627 } | |
| OLD | NEW |