1 // Copyright (c) 2011 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_item.h" 6 7 #include "base/basictypes.h" 8 #include "base/file_util.h" 9 #include "base/format_macros.h" 10 #include "base/logging.h" 11 #include "base/metrics/histogram.h" 12 #include "base/stringprintf.h" 13 #include "base/timer.h" 14 #include "base/utf_string_conversions.h" 15 #include "net/base/net_util.h" 16 #include "chrome/browser/download/download_extensions.h" 17 #include "chrome/browser/download/download_file_manager.h" 18 #include "chrome/browser/download/download_history.h" 19 #include "chrome/browser/download/download_manager.h" 20 #include "chrome/browser/download/download_prefs.h" 21 #include "chrome/browser/download/download_util.h" 22 #include "chrome/browser/history/download_create_info.h" 23 #include "chrome/browser/platform_util.h" 24 #include "chrome/browser/prefs/pref_service.h" 25 #include "chrome/browser/profiles/profile.h" 26 #include "chrome/common/extensions/extension.h" 27 #include "chrome/common/pref_names.h" 28 #include "content/browser/browser_thread.h" 29 #include "ui/base/l10n/l10n_util.h" 30 31 // A DownloadItem normally goes through the following states: 32 // * Created (when download starts) 33 // * Made visible to consumers (e.g. Javascript) after the 34 // destination file has been determined. 35 // * Entered into the history database. 36 // * Made visible in the download shelf. 37 // * All data is saved. Note that the actual data download occurs 38 // in parallel with the above steps, but until those steps are 39 // complete, completion of the data download will be ignored. 40 // * Download file is renamed to its final name, and possibly 41 // auto-opened. 42 // TODO(rdsmith): This progress should be reflected in 43 // DownloadItem::DownloadState and a state transition table/state diagram. 44 // 45 // TODO(rdsmith): This description should be updated to reflect the cancel 46 // pathways. 47 48 namespace { 49 50 // Update frequency (milliseconds). 51 const int kUpdateTimeMs = 1000; 52 53 void DeleteDownloadedFile(const FilePath& path) { 54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 55 56 // Make sure we only delete files. 57 if (!file_util::DirectoryExists(path)) 58 file_util::Delete(path, false); 59 } 60 61 const char* DebugSafetyStateString(DownloadItem::SafetyState state) { 62 switch (state) { 63 case DownloadItem::SAFE: 64 return "SAFE"; 65 case DownloadItem::DANGEROUS: 66 return "DANGEROUS"; 67 case DownloadItem::DANGEROUS_BUT_VALIDATED: 68 return "DANGEROUS_BUT_VALIDATED"; 69 default: 70 NOTREACHED() << "Unknown safety state " << state; 71 return "unknown"; 72 }; 73 } 74 75 const char* DebugDownloadStateString(DownloadItem::DownloadState state) { 76 switch (state) { 77 case DownloadItem::IN_PROGRESS: 78 return "IN_PROGRESS"; 79 case DownloadItem::COMPLETE: 80 return "COMPLETE"; 81 case DownloadItem::CANCELLED: 82 return "CANCELLED"; 83 case DownloadItem::REMOVING: 84 return "REMOVING"; 85 case DownloadItem::INTERRUPTED: 86 return "INTERRUPTED"; 87 default: 88 NOTREACHED() << "Unknown download state " << state; 89 return "unknown"; 90 }; 91 } 92 93 DownloadItem::SafetyState GetSafetyState(bool dangerous_file, 94 bool dangerous_url) { 95 return (dangerous_url || dangerous_file) ? 96 DownloadItem::DANGEROUS : DownloadItem::SAFE; 97 } 98 99 // Note: When a download has both |dangerous_file| and |dangerous_url| set, 100 // danger type is set to DANGEROUS_URL since the risk of dangerous URL 101 // overweights that of dangerous file type. 102 DownloadItem::DangerType GetDangerType(bool dangerous_file, 103 bool dangerous_url) { 104 if (dangerous_url) { 105 // dangerous URL overweights dangerous file. We check dangerous URL first. 106 return DownloadItem::DANGEROUS_URL; 107 } else if (dangerous_file) { 108 return DownloadItem::DANGEROUS_FILE; 109 } 110 return DownloadItem::NOT_DANGEROUS; 111 } 112 113 } // namespace 114 115 // Constructor for reading from the history service. 116 DownloadItem::DownloadItem(DownloadManager* download_manager, 117 const DownloadCreateInfo& info) 118 : id_(-1), 119 full_path_(info.path), 120 path_uniquifier_(0), 121 url_chain_(info.url_chain), 122 referrer_url_(info.referrer_url), 123 mime_type_(info.mime_type), 124 original_mime_type_(info.original_mime_type), 125 total_bytes_(info.total_bytes), 126 received_bytes_(info.received_bytes), 127 start_tick_(base::TimeTicks()), 128 state_(static_cast<DownloadState>(info.state)), 129 start_time_(info.start_time), 130 db_handle_(info.db_handle), 131 download_manager_(download_manager), 132 is_paused_(false), 133 open_when_complete_(false), 134 safety_state_(SAFE), 135 danger_type_(NOT_DANGEROUS), 136 auto_opened_(false), 137 target_name_(info.original_name), 138 render_process_id_(-1), 139 request_id_(-1), 140 save_as_(false), 141 is_otr_(false), 142 is_extension_install_(info.is_extension_install), 143 name_finalized_(false), 144 is_temporary_(false), 145 all_data_saved_(false), 146 opened_(false) { 147 if (IsInProgress()) 148 state_ = CANCELLED; 149 if (IsComplete()) 150 all_data_saved_ = true; 151 Init(false /* don't start progress timer */); 152 } 153 154 // Constructing for a regular download: 155 DownloadItem::DownloadItem(DownloadManager* download_manager, 156 const DownloadCreateInfo& info, 157 bool is_otr) 158 : id_(info.download_id), 159 full_path_(info.path), 160 path_uniquifier_(info.path_uniquifier), 161 url_chain_(info.url_chain), 162 referrer_url_(info.referrer_url), 163 mime_type_(info.mime_type), 164 original_mime_type_(info.original_mime_type), 165 total_bytes_(info.total_bytes), 166 received_bytes_(0), 167 last_os_error_(0), 168 start_tick_(base::TimeTicks::Now()), 169 state_(IN_PROGRESS), 170 start_time_(info.start_time), 171 db_handle_(DownloadHistory::kUninitializedHandle), 172 download_manager_(download_manager), 173 is_paused_(false), 174 open_when_complete_(false), 175 safety_state_(GetSafetyState(info.is_dangerous_file, 176 info.is_dangerous_url)), 177 danger_type_(GetDangerType(info.is_dangerous_file, 178 info.is_dangerous_url)), 179 auto_opened_(false), 180 target_name_(info.original_name), 181 render_process_id_(info.child_id), 182 request_id_(info.request_id), 183 save_as_(info.prompt_user_for_save_location), 184 is_otr_(is_otr), 185 is_extension_install_(info.is_extension_install), 186 name_finalized_(false), 187 is_temporary_(!info.save_info.file_path.empty()), 188 all_data_saved_(false), 189 opened_(false) { 190 Init(true /* start progress timer */); 191 } 192 193 // Constructing for the "Save Page As..." feature: 194 DownloadItem::DownloadItem(DownloadManager* download_manager, 195 const FilePath& path, 196 const GURL& url, 197 bool is_otr) 198 : id_(1), 199 full_path_(path), 200 path_uniquifier_(0), 201 url_chain_(1, url), 202 referrer_url_(GURL()), 203 mime_type_(std::string()), 204 original_mime_type_(std::string()), 205 total_bytes_(0), 206 received_bytes_(0), 207 last_os_error_(0), 208 start_tick_(base::TimeTicks::Now()), 209 state_(IN_PROGRESS), 210 start_time_(base::Time::Now()), 211 db_handle_(DownloadHistory::kUninitializedHandle), 212 download_manager_(download_manager), 213 is_paused_(false), 214 open_when_complete_(false), 215 safety_state_(SAFE), 216 danger_type_(NOT_DANGEROUS), 217 auto_opened_(false), 218 render_process_id_(-1), 219 request_id_(-1), 220 save_as_(false), 221 is_otr_(is_otr), 222 is_extension_install_(false), 223 name_finalized_(false), 224 is_temporary_(false), 225 all_data_saved_(false), 226 opened_(false) { 227 Init(true /* start progress timer */); 228 } 229 230 DownloadItem::~DownloadItem() { 231 state_ = REMOVING; 232 UpdateObservers(); 233 } 234 235 void DownloadItem::AddObserver(Observer* observer) { 236 observers_.AddObserver(observer); 237 } 238 239 void DownloadItem::RemoveObserver(Observer* observer) { 240 observers_.RemoveObserver(observer); 241 } 242 243 void DownloadItem::UpdateObservers() { 244 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); 245 } 246 247 bool DownloadItem::CanOpenDownload() { 248 return !Extension::IsExtension(target_name_); 249 } 250 251 bool DownloadItem::ShouldOpenFileBasedOnExtension() { 252 return download_manager_->ShouldOpenFileBasedOnExtension( 253 GetUserVerifiedFilePath()); 254 } 255 256 void DownloadItem::OpenFilesBasedOnExtension(bool open) { 257 DownloadPrefs* prefs = download_manager_->download_prefs(); 258 if (open) 259 prefs->EnableAutoOpenBasedOnExtension(GetUserVerifiedFilePath()); 260 else 261 prefs->DisableAutoOpenBasedOnExtension(GetUserVerifiedFilePath()); 262 } 263 264 void DownloadItem::OpenDownload() { 265 if (IsPartialDownload()) { 266 open_when_complete_ = !open_when_complete_; 267 } else if (IsComplete()) { 268 opened_ = true; 269 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this)); 270 if (is_extension_install()) { 271 download_util::OpenChromeExtension(download_manager_->profile(), 272 download_manager_, 273 *this); 274 return; 275 } 276 #if defined(OS_MACOSX) 277 // Mac OS X requires opening downloads on the UI thread. 278 platform_util::OpenItem(full_path()); 279 #else 280 BrowserThread::PostTask( 281 BrowserThread::FILE, FROM_HERE, 282 NewRunnableFunction(&platform_util::OpenItem, full_path())); 283 #endif 284 } 285 } 286 287 void DownloadItem::ShowDownloadInShell() { 288 #if defined(OS_MACOSX) 289 // Mac needs to run this operation on the UI thread. 290 platform_util::ShowItemInFolder(full_path()); 291 #else 292 BrowserThread::PostTask( 293 BrowserThread::FILE, FROM_HERE, 294 NewRunnableFunction(&platform_util::ShowItemInFolder, 295 full_path())); 296 #endif 297 } 298 299 void DownloadItem::DangerousDownloadValidated() { 300 UMA_HISTOGRAM_ENUMERATION("Download.DangerousDownloadValidated", 301 danger_type_, 302 DANGEROUS_TYPE_MAX); 303 download_manager_->DangerousDownloadValidated(this); 304 } 305 306 void DownloadItem::UpdateSize(int64 bytes_so_far) { 307 received_bytes_ = bytes_so_far; 308 309 // If we've received more data than we were expecting (bad server info?), 310 // revert to 'unknown size mode'. 311 if (received_bytes_ > total_bytes_) 312 total_bytes_ = 0; 313 } 314 315 void DownloadItem::StartProgressTimer() { 316 update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdateTimeMs), this, 317 &DownloadItem::UpdateObservers); 318 } 319 320 void DownloadItem::StopProgressTimer() { 321 update_timer_.Stop(); 322 } 323 324 // Updates from the download thread may have been posted while this download 325 // was being cancelled in the UI thread, so we'll accept them unless we're 326 // complete. 327 void DownloadItem::Update(int64 bytes_so_far) { 328 if (!IsInProgress()) { 329 NOTREACHED(); 330 return; 331 } 332 UpdateSize(bytes_so_far); 333 UpdateObservers(); 334 } 335 336 // Triggered by a user action. 337 void DownloadItem::Cancel(bool update_history) { 338 VLOG(20) << __FUNCTION__ << "()" << " download = " << DebugString(true); 339 if (!IsPartialDownload()) { 340 // Small downloads might be complete before this method has 341 // a chance to run. 342 return; 343 } 344 345 download_util::RecordDownloadCount(download_util::CANCELLED_COUNT); 346 347 state_ = CANCELLED; 348 UpdateObservers(); 349 StopProgressTimer(); 350 if (update_history) 351 download_manager_->DownloadCancelled(id_); 352 } 353 354 void DownloadItem::MarkAsComplete() { 355 DCHECK(all_data_saved_); 356 state_ = COMPLETE; 357 UpdateObservers(); 358 } 359 360 void DownloadItem::OnAllDataSaved(int64 size) { 361 DCHECK(!all_data_saved_); 362 all_data_saved_ = true; 363 UpdateSize(size); 364 StopProgressTimer(); 365 } 366 367 void DownloadItem::Completed() { 368 VLOG(20) << " " << __FUNCTION__ << "() " 369 << DebugString(false); 370 371 download_util::RecordDownloadCount(download_util::COMPLETED_COUNT); 372 373 // Handle chrome extensions explicitly and skip the shell execute. 374 if (is_extension_install()) { 375 download_util::OpenChromeExtension(download_manager_->profile(), 376 download_manager_, 377 *this); 378 auto_opened_ = true; 379 } else if (open_when_complete() || 380 download_manager_->ShouldOpenFileBasedOnExtension( 381 GetUserVerifiedFilePath()) || 382 is_temporary()) { 383 // If the download is temporary, like in drag-and-drop, do not open it but 384 // we still need to set it auto-opened so that it can be removed from the 385 // download shelf. 386 if (!is_temporary()) 387 OpenDownload(); 388 auto_opened_ = true; 389 } 390 391 DCHECK(all_data_saved_); 392 state_ = COMPLETE; 393 UpdateObservers(); 394 download_manager_->DownloadCompleted(id()); 395 } 396 397 void DownloadItem::Interrupted(int64 size, int os_error) { 398 if (!IsInProgress()) 399 return; 400 state_ = INTERRUPTED; 401 last_os_error_ = os_error; 402 UpdateSize(size); 403 StopProgressTimer(); 404 UpdateObservers(); 405 } 406 407 void DownloadItem::Delete(DeleteReason reason) { 408 switch (reason) { 409 case DELETE_DUE_TO_USER_DISCARD: 410 UMA_HISTOGRAM_ENUMERATION("Download.UserDiscard", 411 danger_type_, 412 DANGEROUS_TYPE_MAX); 413 break; 414 case DELETE_DUE_TO_BROWSER_SHUTDOWN: 415 UMA_HISTOGRAM_ENUMERATION("Download.Discard", 416 danger_type_, 417 DANGEROUS_TYPE_MAX); 418 break; 419 default: 420 NOTREACHED(); 421 } 422 423 BrowserThread::PostTask( 424 BrowserThread::FILE, FROM_HERE, 425 NewRunnableFunction(&DeleteDownloadedFile, full_path_)); 426 Remove(); 427 // We have now been deleted. 428 } 429 430 void DownloadItem::Remove() { 431 Cancel(true); 432 state_ = REMOVING; 433 download_manager_->RemoveDownload(db_handle_); 434 // We have now been deleted. 435 } 436 437 bool DownloadItem::TimeRemaining(base::TimeDelta* remaining) const { 438 if (total_bytes_ <= 0) 439 return false; // We never received the content_length for this download. 440 441 int64 speed = CurrentSpeed(); 442 if (speed == 0) 443 return false; 444 445 *remaining = 446 base::TimeDelta::FromSeconds((total_bytes_ - received_bytes_) / speed); 447 return true; 448 } 449 450 int64 DownloadItem::CurrentSpeed() const { 451 if (is_paused_) 452 return 0; 453 base::TimeDelta diff = base::TimeTicks::Now() - start_tick_; 454 int64 diff_ms = diff.InMilliseconds(); 455 return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms; 456 } 457 458 int DownloadItem::PercentComplete() const { 459 int percent = -1; 460 if (total_bytes_ > 0) 461 percent = static_cast<int>(received_bytes_ * 100.0 / total_bytes_); 462 return percent; 463 } 464 465 void DownloadItem::Rename(const FilePath& full_path) { 466 VLOG(20) << " " << __FUNCTION__ << "()" 467 << " full_path = \"" << full_path.value() << "\"" 468 << DebugString(true); 469 DCHECK(!full_path.empty()); 470 full_path_ = full_path; 471 } 472 473 void DownloadItem::TogglePause() { 474 DCHECK(IsInProgress()); 475 download_manager_->PauseDownload(id_, !is_paused_); 476 is_paused_ = !is_paused_; 477 UpdateObservers(); 478 } 479 480 void DownloadItem::OnNameFinalized() { 481 VLOG(20) << " " << __FUNCTION__ << "() " 482 << DebugString(true); 483 name_finalized_ = true; 484 485 // We can't reach this point in the code without having received all the 486 // data, so it's safe to move to the COMPLETE state. 487 DCHECK(all_data_saved_); 488 state_ = COMPLETE; 489 UpdateObservers(); 490 download_manager_->DownloadCompleted(id()); 491 } 492 493 void DownloadItem::OnDownloadCompleting(DownloadFileManager* file_manager) { 494 VLOG(20) << " " << __FUNCTION__ << "() " 495 << " needs rename = " << NeedsRename() 496 << " " << DebugString(true); 497 DCHECK_NE(DANGEROUS, safety_state()); 498 DCHECK(file_manager); 499 500 if (NeedsRename()) { 501 BrowserThread::PostTask( 502 BrowserThread::FILE, FROM_HERE, 503 NewRunnableMethod( 504 file_manager, &DownloadFileManager::RenameCompletingDownloadFile, 505 id(), GetTargetFilePath(), safety_state() == SAFE)); 506 return; 507 } else { 508 name_finalized_ = true; 509 } 510 511 Completed(); 512 513 BrowserThread::PostTask( 514 BrowserThread::FILE, FROM_HERE, 515 NewRunnableMethod( 516 file_manager, &DownloadFileManager::CompleteDownload, id())); 517 } 518 519 void DownloadItem::OnDownloadRenamedToFinalName(const FilePath& full_path) { 520 VLOG(20) << " " << __FUNCTION__ << "()" 521 << " full_path = " << full_path.value() 522 << " needed rename = " << NeedsRename() 523 << " " << DebugString(false); 524 DCHECK(NeedsRename()); 525 526 Rename(full_path); 527 OnNameFinalized(); 528 529 Completed(); 530 } 531 532 bool DownloadItem::MatchesQuery(const string16& query) const { 533 if (query.empty()) 534 return true; 535 536 DCHECK_EQ(query, l10n_util::ToLower(query)); 537 538 string16 url_raw(l10n_util::ToLower(UTF8ToUTF16(url().spec()))); 539 if (url_raw.find(query) != string16::npos) 540 return true; 541 542 // TODO(phajdan.jr): write a test case for the following code. 543 // A good test case would be: 544 // "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", 545 // L"/\x4f60\x597d\x4f60\x597d", 546 // "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD" 547 PrefService* prefs = download_manager_->profile()->GetPrefs(); 548 std::string languages(prefs->GetString(prefs::kAcceptLanguages)); 549 string16 url_formatted(l10n_util::ToLower(net::FormatUrl(url(), languages))); 550 if (url_formatted.find(query) != string16::npos) 551 return true; 552 553 string16 path(l10n_util::ToLower(full_path().LossyDisplayName())); 554 // This shouldn't just do a substring match; it is wrong for Unicode 555 // due to normalization and we have a fancier search-query system 556 // used elsewhere. 557 // http://code.google.com/p/chromium/issues/detail?id=71982 558 if (path.find(query) != string16::npos) 559 return true; 560 561 return false; 562 } 563 564 void DownloadItem::SetFileCheckResults(const FilePath& path, 565 bool is_dangerous_file, 566 bool is_dangerous_url, 567 int path_uniquifier, 568 bool prompt, 569 bool is_extension_install, 570 const FilePath& original_name) { 571 VLOG(20) << " " << __FUNCTION__ << "()" 572 << " path = \"" << path.value() << "\"" 573 << " is_dangerous_file = " << is_dangerous_file 574 << " is_dangerous_url = " << is_dangerous_url 575 << " path_uniquifier = " << path_uniquifier 576 << " prompt = " << prompt 577 << " is_extension_install = " << is_extension_install 578 << " path = \"" << path.value() << "\"" 579 << " original_name = \"" << original_name.value() << "\"" 580 << " " << DebugString(true); 581 // Make sure the initial file name is set only once. 582 DCHECK(full_path_.empty()); 583 DCHECK(!path.empty()); 584 585 full_path_ = path; 586 safety_state_ = GetSafetyState(is_dangerous_file, is_dangerous_url); 587 danger_type_ = GetDangerType(is_dangerous_file, is_dangerous_url); 588 path_uniquifier_ = path_uniquifier; 589 save_as_ = prompt; 590 is_extension_install_ = is_extension_install; 591 target_name_ = original_name; 592 593 if (target_name_.value().empty()) 594 target_name_ = full_path_.BaseName(); 595 } 596 597 FilePath DownloadItem::GetTargetFilePath() const { 598 return full_path_.DirName().Append(target_name_); 599 } 600 601 FilePath DownloadItem::GetFileNameToReportUser() const { 602 if (path_uniquifier_ > 0) { 603 FilePath name(target_name_); 604 download_util::AppendNumberToPath(&name, path_uniquifier_); 605 return name; 606 } 607 return target_name_; 608 } 609 610 FilePath DownloadItem::GetUserVerifiedFilePath() const { 611 if (safety_state_ == DownloadItem::SAFE) 612 return GetTargetFilePath(); 613 return full_path_; 614 } 615 616 void DownloadItem::Init(bool start_timer) { 617 if (target_name_.value().empty()) 618 target_name_ = full_path_.BaseName(); 619 if (start_timer) 620 StartProgressTimer(); 621 VLOG(20) << " " << __FUNCTION__ << "() " << DebugString(true); 622 } 623 624 // TODO(ahendrickson) -- Move |INTERRUPTED| from |IsCancelled()| to 625 // |IsPartialDownload()|, when resuming interrupted downloads is implemented. 626 bool DownloadItem::IsPartialDownload() const { 627 return (state_ == IN_PROGRESS); 628 } 629 630 bool DownloadItem::IsInProgress() const { 631 return (state_ == IN_PROGRESS); 632 } 633 634 bool DownloadItem::IsCancelled() const { 635 return (state_ == CANCELLED) || (state_ == INTERRUPTED); 636 } 637 638 bool DownloadItem::IsInterrupted() const { 639 return (state_ == INTERRUPTED); 640 } 641 642 bool DownloadItem::IsComplete() const { 643 return state() == COMPLETE; 644 } 645 646 std::string DownloadItem::DebugString(bool verbose) const { 647 std::string description = 648 base::StringPrintf("{ id_ = %d" 649 " state = %s", 650 id_, 651 DebugDownloadStateString(state())); 652 653 if (verbose) { 654 description += base::StringPrintf( 655 " db_handle = %" PRId64 656 " total_bytes = %" PRId64 657 " is_paused = " "%c" 658 " is_extension_install = " "%c" 659 " is_otr = " "%c" 660 " safety_state = " "%s" 661 " url = " "\"%s\"" 662 " target_name_ = \"%" PRFilePath "\"" 663 " full_path = \"%" PRFilePath "\"", 664 db_handle(), 665 total_bytes(), 666 is_paused() ? 'T' : 'F', 667 is_extension_install() ? 'T' : 'F', 668 is_otr() ? 'T' : 'F', 669 DebugSafetyStateString(safety_state()), 670 url().spec().c_str(), 671 target_name_.value().c_str(), 672 full_path().value().c_str()); 673 } else { 674 description += base::StringPrintf(" url = \"%s\"", url().spec().c_str()); 675 } 676 677 description += " }"; 678 679 return description; 680 } 681