1 // Copyright (c) 2012 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 // File method ordering: Methods in this file are in the same order as 6 // in download_item_impl.h, with the following exception: The public 7 // interface Start is placed in chronological order with the other 8 // (private) routines that together define a DownloadItem's state 9 // transitions as the download progresses. See "Download progression 10 // cascade" later in this file. 11 12 // A regular DownloadItem (created for a download in this session of the 13 // browser) normally goes through the following states: 14 // * Created (when download starts) 15 // * Destination filename determined 16 // * Entered into the history database. 17 // * Made visible in the download shelf. 18 // * All the data is saved. Note that the actual data download occurs 19 // in parallel with the above steps, but until those steps are 20 // complete, the state of the data save will be ignored. 21 // * Download file is renamed to its final name, and possibly 22 // auto-opened. 23 24 #include "content/browser/download/download_item_impl.h" 25 26 #include <vector> 27 28 #include "base/basictypes.h" 29 #include "base/bind.h" 30 #include "base/command_line.h" 31 #include "base/file_util.h" 32 #include "base/format_macros.h" 33 #include "base/logging.h" 34 #include "base/metrics/histogram.h" 35 #include "base/stl_util.h" 36 #include "base/strings/stringprintf.h" 37 #include "base/strings/utf_string_conversions.h" 38 #include "content/browser/download/download_create_info.h" 39 #include "content/browser/download/download_file.h" 40 #include "content/browser/download/download_interrupt_reasons_impl.h" 41 #include "content/browser/download/download_item_impl_delegate.h" 42 #include "content/browser/download/download_request_handle.h" 43 #include "content/browser/download/download_stats.h" 44 #include "content/browser/renderer_host/render_view_host_impl.h" 45 #include "content/browser/web_contents/web_contents_impl.h" 46 #include "content/public/browser/browser_context.h" 47 #include "content/public/browser/browser_thread.h" 48 #include "content/public/browser/content_browser_client.h" 49 #include "content/public/browser/download_danger_type.h" 50 #include "content/public/browser/download_interrupt_reasons.h" 51 #include "content/public/browser/download_url_parameters.h" 52 #include "content/public/common/content_switches.h" 53 #include "content/public/common/referrer.h" 54 #include "net/base/net_util.h" 55 56 namespace content { 57 58 namespace { 59 60 void DeleteDownloadedFile(const base::FilePath& path) { 61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 62 63 // Make sure we only delete files. 64 if (!base::DirectoryExists(path)) 65 base::DeleteFile(path, false); 66 } 67 68 // Wrapper around DownloadFile::Detach and DownloadFile::Cancel that 69 // takes ownership of the DownloadFile and hence implicitly destroys it 70 // at the end of the function. 71 static base::FilePath DownloadFileDetach( 72 scoped_ptr<DownloadFile> download_file) { 73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 74 base::FilePath full_path = download_file->FullPath(); 75 download_file->Detach(); 76 return full_path; 77 } 78 79 static void DownloadFileCancel(scoped_ptr<DownloadFile> download_file) { 80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 81 download_file->Cancel(); 82 } 83 84 bool IsDownloadResumptionEnabled() { 85 return CommandLine::ForCurrentProcess()->HasSwitch( 86 switches::kEnableDownloadResumption); 87 } 88 89 } // namespace 90 91 const uint32 DownloadItem::kInvalidId = 0; 92 93 const char DownloadItem::kEmptyFileHash[] = ""; 94 95 // The maximum number of attempts we will make to resume automatically. 96 const int DownloadItemImpl::kMaxAutoResumeAttempts = 5; 97 98 // Constructor for reading from the history service. 99 DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, 100 uint32 download_id, 101 const base::FilePath& current_path, 102 const base::FilePath& target_path, 103 const std::vector<GURL>& url_chain, 104 const GURL& referrer_url, 105 const base::Time& start_time, 106 const base::Time& end_time, 107 const std::string& etag, 108 const std::string& last_modified, 109 int64 received_bytes, 110 int64 total_bytes, 111 DownloadItem::DownloadState state, 112 DownloadDangerType danger_type, 113 DownloadInterruptReason interrupt_reason, 114 bool opened, 115 const net::BoundNetLog& bound_net_log) 116 : is_save_package_download_(false), 117 download_id_(download_id), 118 current_path_(current_path), 119 target_path_(target_path), 120 target_disposition_(TARGET_DISPOSITION_OVERWRITE), 121 url_chain_(url_chain), 122 referrer_url_(referrer_url), 123 transition_type_(PAGE_TRANSITION_LINK), 124 has_user_gesture_(false), 125 total_bytes_(total_bytes), 126 received_bytes_(received_bytes), 127 bytes_per_sec_(0), 128 last_modified_time_(last_modified), 129 etag_(etag), 130 last_reason_(interrupt_reason), 131 start_tick_(base::TimeTicks()), 132 state_(ExternalToInternalState(state)), 133 danger_type_(danger_type), 134 start_time_(start_time), 135 end_time_(end_time), 136 delegate_(delegate), 137 is_paused_(false), 138 auto_resume_count_(0), 139 open_when_complete_(false), 140 file_externally_removed_(false), 141 auto_opened_(false), 142 is_temporary_(false), 143 all_data_saved_(state == COMPLETE), 144 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE), 145 opened_(opened), 146 delegate_delayed_complete_(false), 147 bound_net_log_(bound_net_log), 148 weak_ptr_factory_(this) { 149 delegate_->Attach(); 150 DCHECK_NE(IN_PROGRESS_INTERNAL, state_); 151 Init(false /* not actively downloading */, SRC_HISTORY_IMPORT); 152 } 153 154 // Constructing for a regular download: 155 DownloadItemImpl::DownloadItemImpl( 156 DownloadItemImplDelegate* delegate, 157 uint32 download_id, 158 const DownloadCreateInfo& info, 159 const net::BoundNetLog& bound_net_log) 160 : is_save_package_download_(false), 161 download_id_(download_id), 162 target_disposition_( 163 (info.save_info->prompt_for_save_location) ? 164 TARGET_DISPOSITION_PROMPT : TARGET_DISPOSITION_OVERWRITE), 165 url_chain_(info.url_chain), 166 referrer_url_(info.referrer_url), 167 suggested_filename_(UTF16ToUTF8(info.save_info->suggested_name)), 168 forced_file_path_(info.save_info->file_path), 169 transition_type_(info.transition_type), 170 has_user_gesture_(info.has_user_gesture), 171 content_disposition_(info.content_disposition), 172 mime_type_(info.mime_type), 173 original_mime_type_(info.original_mime_type), 174 remote_address_(info.remote_address), 175 total_bytes_(info.total_bytes), 176 received_bytes_(0), 177 bytes_per_sec_(0), 178 last_modified_time_(info.last_modified), 179 etag_(info.etag), 180 last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE), 181 start_tick_(base::TimeTicks::Now()), 182 state_(IN_PROGRESS_INTERNAL), 183 danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS), 184 start_time_(info.start_time), 185 delegate_(delegate), 186 is_paused_(false), 187 auto_resume_count_(0), 188 open_when_complete_(false), 189 file_externally_removed_(false), 190 auto_opened_(false), 191 is_temporary_(!info.save_info->file_path.empty()), 192 all_data_saved_(false), 193 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE), 194 opened_(false), 195 delegate_delayed_complete_(false), 196 bound_net_log_(bound_net_log), 197 weak_ptr_factory_(this) { 198 delegate_->Attach(); 199 Init(true /* actively downloading */, SRC_ACTIVE_DOWNLOAD); 200 201 // Link the event sources. 202 bound_net_log_.AddEvent( 203 net::NetLog::TYPE_DOWNLOAD_URL_REQUEST, 204 info.request_bound_net_log.source().ToEventParametersCallback()); 205 206 info.request_bound_net_log.AddEvent( 207 net::NetLog::TYPE_DOWNLOAD_STARTED, 208 bound_net_log_.source().ToEventParametersCallback()); 209 } 210 211 // Constructing for the "Save Page As..." feature: 212 DownloadItemImpl::DownloadItemImpl( 213 DownloadItemImplDelegate* delegate, 214 uint32 download_id, 215 const base::FilePath& path, 216 const GURL& url, 217 const std::string& mime_type, 218 scoped_ptr<DownloadRequestHandleInterface> request_handle, 219 const net::BoundNetLog& bound_net_log) 220 : is_save_package_download_(true), 221 request_handle_(request_handle.Pass()), 222 download_id_(download_id), 223 current_path_(path), 224 target_path_(path), 225 target_disposition_(TARGET_DISPOSITION_OVERWRITE), 226 url_chain_(1, url), 227 referrer_url_(GURL()), 228 transition_type_(PAGE_TRANSITION_LINK), 229 has_user_gesture_(false), 230 mime_type_(mime_type), 231 original_mime_type_(mime_type), 232 total_bytes_(0), 233 received_bytes_(0), 234 bytes_per_sec_(0), 235 last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE), 236 start_tick_(base::TimeTicks::Now()), 237 state_(IN_PROGRESS_INTERNAL), 238 danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS), 239 start_time_(base::Time::Now()), 240 delegate_(delegate), 241 is_paused_(false), 242 auto_resume_count_(0), 243 open_when_complete_(false), 244 file_externally_removed_(false), 245 auto_opened_(false), 246 is_temporary_(false), 247 all_data_saved_(false), 248 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE), 249 opened_(false), 250 delegate_delayed_complete_(false), 251 bound_net_log_(bound_net_log), 252 weak_ptr_factory_(this) { 253 delegate_->Attach(); 254 Init(true /* actively downloading */, SRC_SAVE_PAGE_AS); 255 } 256 257 DownloadItemImpl::~DownloadItemImpl() { 258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 259 260 // Should always have been nuked before now, at worst in 261 // DownloadManager shutdown. 262 DCHECK(!download_file_.get()); 263 264 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadDestroyed(this)); 265 delegate_->AssertStateConsistent(this); 266 delegate_->Detach(); 267 } 268 269 void DownloadItemImpl::AddObserver(Observer* observer) { 270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 271 272 observers_.AddObserver(observer); 273 } 274 275 void DownloadItemImpl::RemoveObserver(Observer* observer) { 276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 277 278 observers_.RemoveObserver(observer); 279 } 280 281 void DownloadItemImpl::UpdateObservers() { 282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 283 284 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); 285 } 286 287 void DownloadItemImpl::ValidateDangerousDownload() { 288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 289 DCHECK(!IsDone()); 290 DCHECK(IsDangerous()); 291 292 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true); 293 294 if (IsDone() || !IsDangerous()) 295 return; 296 297 RecordDangerousDownloadAccept(GetDangerType(), 298 GetTargetFilePath()); 299 300 danger_type_ = DOWNLOAD_DANGER_TYPE_USER_VALIDATED; 301 302 bound_net_log_.AddEvent( 303 net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED, 304 base::Bind(&ItemCheckedNetLogCallback, GetDangerType())); 305 306 UpdateObservers(); 307 308 MaybeCompleteDownload(); 309 } 310 311 void DownloadItemImpl::StealDangerousDownload( 312 const AcquireFileCallback& callback) { 313 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); 314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 315 DCHECK(IsDangerous()); 316 if (download_file_) { 317 BrowserThread::PostTaskAndReplyWithResult( 318 BrowserThread::FILE, 319 FROM_HERE, 320 base::Bind(&DownloadFileDetach, base::Passed(&download_file_)), 321 callback); 322 } else { 323 callback.Run(current_path_); 324 } 325 current_path_.clear(); 326 Remove(); 327 // We have now been deleted. 328 } 329 330 void DownloadItemImpl::Pause() { 331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 332 333 // Ignore irrelevant states. 334 if (state_ != IN_PROGRESS_INTERNAL || is_paused_) 335 return; 336 337 request_handle_->PauseRequest(); 338 is_paused_ = true; 339 UpdateObservers(); 340 } 341 342 void DownloadItemImpl::Resume() { 343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 344 switch (state_) { 345 case IN_PROGRESS_INTERNAL: 346 if (!is_paused_) 347 return; 348 request_handle_->ResumeRequest(); 349 is_paused_ = false; 350 UpdateObservers(); 351 return; 352 353 case COMPLETING_INTERNAL: 354 case COMPLETE_INTERNAL: 355 case CANCELLED_INTERNAL: 356 case RESUMING_INTERNAL: 357 return; 358 359 case INTERRUPTED_INTERNAL: 360 auto_resume_count_ = 0; // User input resets the counter. 361 ResumeInterruptedDownload(); 362 return; 363 364 case MAX_DOWNLOAD_INTERNAL_STATE: 365 NOTREACHED(); 366 } 367 } 368 369 void DownloadItemImpl::Cancel(bool user_cancel) { 370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 371 372 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); 373 if (state_ != IN_PROGRESS_INTERNAL && 374 state_ != INTERRUPTED_INTERNAL && 375 state_ != RESUMING_INTERNAL) { 376 // Small downloads might be complete before this method has a chance to run. 377 return; 378 } 379 380 if (IsDangerous()) { 381 RecordDangerousDownloadDiscard( 382 user_cancel ? DOWNLOAD_DISCARD_DUE_TO_USER_ACTION 383 : DOWNLOAD_DISCARD_DUE_TO_SHUTDOWN, 384 GetDangerType(), 385 GetTargetFilePath()); 386 } 387 388 last_reason_ = user_cancel ? DOWNLOAD_INTERRUPT_REASON_USER_CANCELED 389 : DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN; 390 391 RecordDownloadCount(CANCELLED_COUNT); 392 393 // TODO(rdsmith/benjhayden): Remove condition as part of 394 // |SavePackage| integration. 395 // |download_file_| can be NULL if Interrupt() is called after the 396 // download file has been released. 397 if (!is_save_package_download_ && download_file_) 398 ReleaseDownloadFile(true); 399 400 if (state_ == IN_PROGRESS_INTERNAL) { 401 // Cancel the originating URL request unless it's already been cancelled 402 // by interrupt. 403 request_handle_->CancelRequest(); 404 } 405 406 // Remove the intermediate file if we are cancelling an interrupted download. 407 // Continuable interruptions leave the intermediate file around. 408 if ((state_ == INTERRUPTED_INTERNAL || state_ == RESUMING_INTERNAL) && 409 !current_path_.empty()) { 410 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 411 base::Bind(&DeleteDownloadedFile, current_path_)); 412 current_path_.clear(); 413 } 414 415 TransitionTo(CANCELLED_INTERNAL, UPDATE_OBSERVERS); 416 } 417 418 void DownloadItemImpl::Remove() { 419 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); 420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 421 422 delegate_->AssertStateConsistent(this); 423 Cancel(true); 424 delegate_->AssertStateConsistent(this); 425 426 NotifyRemoved(); 427 delegate_->DownloadRemoved(this); 428 // We have now been deleted. 429 } 430 431 void DownloadItemImpl::OpenDownload() { 432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 433 434 if (!IsDone()) { 435 // We don't honor the open_when_complete_ flag for temporary 436 // downloads. Don't set it because it shows up in the UI. 437 if (!IsTemporary()) 438 open_when_complete_ = !open_when_complete_; 439 return; 440 } 441 442 if (state_ != COMPLETE_INTERNAL || file_externally_removed_) 443 return; 444 445 // Ideally, we want to detect errors in opening and report them, but we 446 // don't generally have the proper interface for that to the external 447 // program that opens the file. So instead we spawn a check to update 448 // the UI if the file has been deleted in parallel with the open. 449 delegate_->CheckForFileRemoval(this); 450 RecordOpen(GetEndTime(), !GetOpened()); 451 opened_ = true; 452 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this)); 453 delegate_->OpenDownload(this); 454 } 455 456 void DownloadItemImpl::ShowDownloadInShell() { 457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 458 459 delegate_->ShowDownloadInShell(this); 460 } 461 462 uint32 DownloadItemImpl::GetId() const { 463 return download_id_; 464 } 465 466 DownloadItem::DownloadState DownloadItemImpl::GetState() const { 467 return InternalToExternalState(state_); 468 } 469 470 DownloadInterruptReason DownloadItemImpl::GetLastReason() const { 471 return last_reason_; 472 } 473 474 bool DownloadItemImpl::IsPaused() const { 475 return is_paused_; 476 } 477 478 bool DownloadItemImpl::IsTemporary() const { 479 return is_temporary_; 480 } 481 482 bool DownloadItemImpl::CanResume() const { 483 if ((GetState() == IN_PROGRESS) && IsPaused()) 484 return true; 485 486 if (state_ != INTERRUPTED_INTERNAL) 487 return false; 488 489 // Downloads that don't have a WebContents should still be resumable, but this 490 // isn't currently the case. See ResumeInterruptedDownload(). 491 if (!GetWebContents()) 492 return false; 493 494 ResumeMode resume_mode = GetResumeMode(); 495 return IsDownloadResumptionEnabled() && 496 (resume_mode == RESUME_MODE_USER_RESTART || 497 resume_mode == RESUME_MODE_USER_CONTINUE); 498 } 499 500 bool DownloadItemImpl::IsDone() const { 501 switch (state_) { 502 case IN_PROGRESS_INTERNAL: 503 case COMPLETING_INTERNAL: 504 return false; 505 506 case COMPLETE_INTERNAL: 507 case CANCELLED_INTERNAL: 508 return true; 509 510 case INTERRUPTED_INTERNAL: 511 return !CanResume(); 512 513 case RESUMING_INTERNAL: 514 return false; 515 516 case MAX_DOWNLOAD_INTERNAL_STATE: 517 break; 518 } 519 NOTREACHED(); 520 return true; 521 } 522 523 const GURL& DownloadItemImpl::GetURL() const { 524 return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.back(); 525 } 526 527 const std::vector<GURL>& DownloadItemImpl::GetUrlChain() const { 528 return url_chain_; 529 } 530 531 const GURL& DownloadItemImpl::GetOriginalUrl() const { 532 // Be careful about taking the front() of possibly-empty vectors! 533 // http://crbug.com/190096 534 return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.front(); 535 } 536 537 const GURL& DownloadItemImpl::GetReferrerUrl() const { 538 return referrer_url_; 539 } 540 541 std::string DownloadItemImpl::GetSuggestedFilename() const { 542 return suggested_filename_; 543 } 544 545 std::string DownloadItemImpl::GetContentDisposition() const { 546 return content_disposition_; 547 } 548 549 std::string DownloadItemImpl::GetMimeType() const { 550 return mime_type_; 551 } 552 553 std::string DownloadItemImpl::GetOriginalMimeType() const { 554 return original_mime_type_; 555 } 556 557 std::string DownloadItemImpl::GetRemoteAddress() const { 558 return remote_address_; 559 } 560 561 bool DownloadItemImpl::HasUserGesture() const { 562 return has_user_gesture_; 563 }; 564 565 PageTransition DownloadItemImpl::GetTransitionType() const { 566 return transition_type_; 567 }; 568 569 const std::string& DownloadItemImpl::GetLastModifiedTime() const { 570 return last_modified_time_; 571 } 572 573 const std::string& DownloadItemImpl::GetETag() const { 574 return etag_; 575 } 576 577 bool DownloadItemImpl::IsSavePackageDownload() const { 578 return is_save_package_download_; 579 } 580 581 const base::FilePath& DownloadItemImpl::GetFullPath() const { 582 return current_path_; 583 } 584 585 const base::FilePath& DownloadItemImpl::GetTargetFilePath() const { 586 return target_path_; 587 } 588 589 const base::FilePath& DownloadItemImpl::GetForcedFilePath() const { 590 // TODO(asanka): Get rid of GetForcedFilePath(). We should instead just 591 // require that clients respect GetTargetFilePath() if it is already set. 592 return forced_file_path_; 593 } 594 595 base::FilePath DownloadItemImpl::GetFileNameToReportUser() const { 596 if (!display_name_.empty()) 597 return display_name_; 598 return target_path_.BaseName(); 599 } 600 601 DownloadItem::TargetDisposition DownloadItemImpl::GetTargetDisposition() const { 602 return target_disposition_; 603 } 604 605 const std::string& DownloadItemImpl::GetHash() const { 606 return hash_; 607 } 608 609 const std::string& DownloadItemImpl::GetHashState() const { 610 return hash_state_; 611 } 612 613 bool DownloadItemImpl::GetFileExternallyRemoved() const { 614 return file_externally_removed_; 615 } 616 617 void DownloadItemImpl::DeleteFile() { 618 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 619 if ((GetState() != DownloadItem::COMPLETE) || 620 file_externally_removed_) { 621 return; 622 } 623 BrowserThread::PostTaskAndReply( 624 BrowserThread::FILE, FROM_HERE, 625 base::Bind(&DeleteDownloadedFile, current_path_), 626 base::Bind(&DownloadItemImpl::OnDownloadedFileRemoved, 627 weak_ptr_factory_.GetWeakPtr())); 628 current_path_.clear(); 629 } 630 631 bool DownloadItemImpl::IsDangerous() const { 632 #if defined(OS_WIN) 633 // TODO(noelutz): At this point only the windows views UI supports 634 // warnings based on dangerous content. 635 return (danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE || 636 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL || 637 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT || 638 danger_type_ == DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT || 639 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST || 640 danger_type_ == DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED); 641 #else 642 return (danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE || 643 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL); 644 #endif 645 } 646 647 DownloadDangerType DownloadItemImpl::GetDangerType() const { 648 return danger_type_; 649 } 650 651 bool DownloadItemImpl::TimeRemaining(base::TimeDelta* remaining) const { 652 if (total_bytes_ <= 0) 653 return false; // We never received the content_length for this download. 654 655 int64 speed = CurrentSpeed(); 656 if (speed == 0) 657 return false; 658 659 *remaining = base::TimeDelta::FromSeconds( 660 (total_bytes_ - received_bytes_) / speed); 661 return true; 662 } 663 664 int64 DownloadItemImpl::CurrentSpeed() const { 665 if (is_paused_) 666 return 0; 667 return bytes_per_sec_; 668 } 669 670 int DownloadItemImpl::PercentComplete() const { 671 // If the delegate is delaying completion of the download, then we have no 672 // idea how long it will take. 673 if (delegate_delayed_complete_ || total_bytes_ <= 0) 674 return -1; 675 676 return static_cast<int>(received_bytes_ * 100.0 / total_bytes_); 677 } 678 679 bool DownloadItemImpl::AllDataSaved() const { 680 return all_data_saved_; 681 } 682 683 int64 DownloadItemImpl::GetTotalBytes() const { 684 return total_bytes_; 685 } 686 687 int64 DownloadItemImpl::GetReceivedBytes() const { 688 return received_bytes_; 689 } 690 691 base::Time DownloadItemImpl::GetStartTime() const { 692 return start_time_; 693 } 694 695 base::Time DownloadItemImpl::GetEndTime() const { 696 return end_time_; 697 } 698 699 bool DownloadItemImpl::CanShowInFolder() { 700 // A download can be shown in the folder if the downloaded file is in a known 701 // location. 702 return CanOpenDownload() && !GetFullPath().empty(); 703 } 704 705 bool DownloadItemImpl::CanOpenDownload() { 706 // We can open the file or mark it for opening on completion if the download 707 // is expected to complete successfully. Exclude temporary downloads, since 708 // they aren't owned by the download system. 709 const bool is_complete = GetState() == DownloadItem::COMPLETE; 710 return (!IsDone() || is_complete) && !IsTemporary() && 711 !file_externally_removed_; 712 } 713 714 bool DownloadItemImpl::ShouldOpenFileBasedOnExtension() { 715 return delegate_->ShouldOpenFileBasedOnExtension(GetTargetFilePath()); 716 } 717 718 bool DownloadItemImpl::GetOpenWhenComplete() const { 719 return open_when_complete_; 720 } 721 722 bool DownloadItemImpl::GetAutoOpened() { 723 return auto_opened_; 724 } 725 726 bool DownloadItemImpl::GetOpened() const { 727 return opened_; 728 } 729 730 BrowserContext* DownloadItemImpl::GetBrowserContext() const { 731 return delegate_->GetBrowserContext(); 732 } 733 734 WebContents* DownloadItemImpl::GetWebContents() const { 735 // TODO(rdsmith): Remove null check after removing GetWebContents() from 736 // paths that might be used by DownloadItems created from history import. 737 // Currently such items have null request_handle_s, where other items 738 // (regular and SavePackage downloads) have actual objects off the pointer. 739 if (request_handle_) 740 return request_handle_->GetWebContents(); 741 return NULL; 742 } 743 744 void DownloadItemImpl::OnContentCheckCompleted(DownloadDangerType danger_type) { 745 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 746 DCHECK(AllDataSaved()); 747 VLOG(20) << __FUNCTION__ << " danger_type=" << danger_type 748 << " download=" << DebugString(true); 749 SetDangerType(danger_type); 750 UpdateObservers(); 751 } 752 753 void DownloadItemImpl::SetOpenWhenComplete(bool open) { 754 open_when_complete_ = open; 755 } 756 757 void DownloadItemImpl::SetIsTemporary(bool temporary) { 758 is_temporary_ = temporary; 759 } 760 761 void DownloadItemImpl::SetOpened(bool opened) { 762 opened_ = opened; 763 } 764 765 void DownloadItemImpl::SetDisplayName(const base::FilePath& name) { 766 display_name_ = name; 767 } 768 769 std::string DownloadItemImpl::DebugString(bool verbose) const { 770 std::string description = 771 base::StringPrintf("{ id = %d" 772 " state = %s", 773 download_id_, 774 DebugDownloadStateString(state_)); 775 776 // Construct a string of the URL chain. 777 std::string url_list("<none>"); 778 if (!url_chain_.empty()) { 779 std::vector<GURL>::const_iterator iter = url_chain_.begin(); 780 std::vector<GURL>::const_iterator last = url_chain_.end(); 781 url_list = (*iter).is_valid() ? (*iter).spec() : "<invalid>"; 782 ++iter; 783 for ( ; verbose && (iter != last); ++iter) { 784 url_list += " ->\n\t"; 785 const GURL& next_url = *iter; 786 url_list += next_url.is_valid() ? next_url.spec() : "<invalid>"; 787 } 788 } 789 790 if (verbose) { 791 description += base::StringPrintf( 792 " total = %" PRId64 793 " received = %" PRId64 794 " reason = %s" 795 " paused = %c" 796 " resume_mode = %s" 797 " auto_resume_count = %d" 798 " danger = %d" 799 " all_data_saved = %c" 800 " last_modified = '%s'" 801 " etag = '%s'" 802 " has_download_file = %s" 803 " url_chain = \n\t\"%s\"\n\t" 804 " full_path = \"%" PRFilePath "\"\n\t" 805 " target_path = \"%" PRFilePath "\"", 806 GetTotalBytes(), 807 GetReceivedBytes(), 808 InterruptReasonDebugString(last_reason_).c_str(), 809 IsPaused() ? 'T' : 'F', 810 DebugResumeModeString(GetResumeMode()), 811 auto_resume_count_, 812 GetDangerType(), 813 AllDataSaved() ? 'T' : 'F', 814 GetLastModifiedTime().c_str(), 815 GetETag().c_str(), 816 download_file_.get() ? "true" : "false", 817 url_list.c_str(), 818 GetFullPath().value().c_str(), 819 GetTargetFilePath().value().c_str()); 820 } else { 821 description += base::StringPrintf(" url = \"%s\"", url_list.c_str()); 822 } 823 824 description += " }"; 825 826 return description; 827 } 828 829 DownloadItemImpl::ResumeMode DownloadItemImpl::GetResumeMode() const { 830 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 831 // We can't continue without a handle on the intermediate file. 832 // We also can't continue if we don't have some verifier to make sure 833 // we're getting the same file. 834 const bool force_restart = 835 (current_path_.empty() || (etag_.empty() && last_modified_time_.empty())); 836 837 // We won't auto-restart if we've used up our attempts or the 838 // download has been paused by user action. 839 const bool force_user = 840 (auto_resume_count_ >= kMaxAutoResumeAttempts || is_paused_); 841 842 ResumeMode mode = RESUME_MODE_INVALID; 843 844 switch(last_reason_) { 845 case DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR: 846 case DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT: 847 if (force_restart && force_user) 848 mode = RESUME_MODE_USER_RESTART; 849 else if (force_restart) 850 mode = RESUME_MODE_IMMEDIATE_RESTART; 851 else if (force_user) 852 mode = RESUME_MODE_USER_CONTINUE; 853 else 854 mode = RESUME_MODE_IMMEDIATE_CONTINUE; 855 break; 856 857 case DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION: 858 case DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE: 859 case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT: 860 if (force_user) 861 mode = RESUME_MODE_USER_RESTART; 862 else 863 mode = RESUME_MODE_IMMEDIATE_RESTART; 864 break; 865 866 case DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED: 867 case DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED: 868 case DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN: 869 case DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED: 870 case DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN: 871 case DOWNLOAD_INTERRUPT_REASON_CRASH: 872 if (force_restart) 873 mode = RESUME_MODE_USER_RESTART; 874 else 875 mode = RESUME_MODE_USER_CONTINUE; 876 break; 877 878 case DOWNLOAD_INTERRUPT_REASON_FILE_FAILED: 879 case DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED: 880 case DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE: 881 case DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG: 882 case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE: 883 mode = RESUME_MODE_USER_RESTART; 884 break; 885 886 case DOWNLOAD_INTERRUPT_REASON_NONE: 887 case DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED: 888 case DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT: 889 case DOWNLOAD_INTERRUPT_REASON_USER_CANCELED: 890 case DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED: 891 case DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED: 892 mode = RESUME_MODE_INVALID; 893 break; 894 } 895 896 return mode; 897 } 898 899 void DownloadItemImpl::MergeOriginInfoOnResume( 900 const DownloadCreateInfo& new_create_info) { 901 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 902 DCHECK_EQ(RESUMING_INTERNAL, state_); 903 DCHECK(!new_create_info.url_chain.empty()); 904 905 // We are going to tack on any new redirects to our list of redirects. 906 // When a download is resumed, the URL used for the resumption request is the 907 // one at the end of the previous redirect chain. Tacking additional redirects 908 // to the end of this chain ensures that: 909 // - If the download needs to be resumed again, the ETag/Last-Modified headers 910 // will be used with the last server that sent them to us. 911 // - The redirect chain contains all the servers that were involved in this 912 // download since the initial request, in order. 913 std::vector<GURL>::const_iterator chain_iter = 914 new_create_info.url_chain.begin(); 915 if (*chain_iter == url_chain_.back()) 916 ++chain_iter; 917 918 // Record some stats. If the precondition failed (the server returned 919 // HTTP_PRECONDITION_FAILED), then the download will automatically retried as 920 // a full request rather than a partial. Full restarts clobber validators. 921 int origin_state = 0; 922 if (chain_iter != new_create_info.url_chain.end()) 923 origin_state |= ORIGIN_STATE_ON_RESUMPTION_ADDITIONAL_REDIRECTS; 924 if (etag_ != new_create_info.etag || 925 last_modified_time_ != new_create_info.last_modified) 926 origin_state |= ORIGIN_STATE_ON_RESUMPTION_VALIDATORS_CHANGED; 927 if (content_disposition_ != new_create_info.content_disposition) 928 origin_state |= ORIGIN_STATE_ON_RESUMPTION_CONTENT_DISPOSITION_CHANGED; 929 RecordOriginStateOnResumption(new_create_info.save_info->offset != 0, 930 origin_state); 931 932 url_chain_.insert( 933 url_chain_.end(), chain_iter, new_create_info.url_chain.end()); 934 etag_ = new_create_info.etag; 935 last_modified_time_ = new_create_info.last_modified; 936 content_disposition_ = new_create_info.content_disposition; 937 938 // Don't update observers. This method is expected to be called just before a 939 // DownloadFile is created and Start() is called. The observers will be 940 // notified when the download transitions to the IN_PROGRESS state. 941 } 942 943 void DownloadItemImpl::NotifyRemoved() { 944 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadRemoved(this)); 945 } 946 947 void DownloadItemImpl::OnDownloadedFileRemoved() { 948 file_externally_removed_ = true; 949 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true); 950 UpdateObservers(); 951 } 952 953 base::WeakPtr<DownloadDestinationObserver> 954 DownloadItemImpl::DestinationObserverAsWeakPtr() { 955 return weak_ptr_factory_.GetWeakPtr(); 956 } 957 958 const net::BoundNetLog& DownloadItemImpl::GetBoundNetLog() const { 959 return bound_net_log_; 960 } 961 962 void DownloadItemImpl::SetTotalBytes(int64 total_bytes) { 963 total_bytes_ = total_bytes; 964 } 965 966 void DownloadItemImpl::OnAllDataSaved(const std::string& final_hash) { 967 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 968 969 DCHECK_EQ(IN_PROGRESS_INTERNAL, state_); 970 DCHECK(!all_data_saved_); 971 all_data_saved_ = true; 972 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true); 973 974 // Store final hash and null out intermediate serialized hash state. 975 hash_ = final_hash; 976 hash_state_ = ""; 977 978 UpdateObservers(); 979 } 980 981 void DownloadItemImpl::MarkAsComplete() { 982 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 983 984 DCHECK(all_data_saved_); 985 end_time_ = base::Time::Now(); 986 TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS); 987 } 988 989 void DownloadItemImpl::DestinationUpdate(int64 bytes_so_far, 990 int64 bytes_per_sec, 991 const std::string& hash_state) { 992 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 993 VLOG(20) << __FUNCTION__ << " so_far=" << bytes_so_far 994 << " per_sec=" << bytes_per_sec << " download=" << DebugString(true); 995 996 if (GetState() != IN_PROGRESS) { 997 // Ignore if we're no longer in-progress. This can happen if we race a 998 // Cancel on the UI thread with an update on the FILE thread. 999 // 1000 // TODO(rdsmith): Arguably we should let this go through, as this means 1001 // the download really did get further than we know before it was 1002 // cancelled. But the gain isn't very large, and the code is more 1003 // fragile if it has to support in progress updates in a non-in-progress 1004 // state. This issue should be readdressed when we revamp performance 1005 // reporting. 1006 return; 1007 } 1008 bytes_per_sec_ = bytes_per_sec; 1009 hash_state_ = hash_state; 1010 received_bytes_ = bytes_so_far; 1011 1012 // If we've received more data than we were expecting (bad server info?), 1013 // revert to 'unknown size mode'. 1014 if (received_bytes_ > total_bytes_) 1015 total_bytes_ = 0; 1016 1017 if (bound_net_log_.IsLoggingAllEvents()) { 1018 bound_net_log_.AddEvent( 1019 net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED, 1020 net::NetLog::Int64Callback("bytes_so_far", received_bytes_)); 1021 } 1022 1023 UpdateObservers(); 1024 } 1025 1026 void DownloadItemImpl::DestinationError(DownloadInterruptReason reason) { 1027 // Postpone recognition of this error until after file name determination 1028 // has completed and the intermediate file has been renamed to simplify 1029 // resumption conditions. 1030 if (current_path_.empty() || target_path_.empty()) 1031 destination_error_ = reason; 1032 else 1033 Interrupt(reason); 1034 } 1035 1036 void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) { 1037 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true); 1038 if (GetState() != IN_PROGRESS) 1039 return; 1040 OnAllDataSaved(final_hash); 1041 MaybeCompleteDownload(); 1042 } 1043 1044 // **** Download progression cascade 1045 1046 void DownloadItemImpl::Init(bool active, 1047 DownloadType download_type) { 1048 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1049 1050 if (active) 1051 RecordDownloadCount(START_COUNT); 1052 1053 std::string file_name; 1054 if (download_type == SRC_HISTORY_IMPORT) { 1055 // target_path_ works for History and Save As versions. 1056 file_name = target_path_.AsUTF8Unsafe(); 1057 } else { 1058 // See if it's set programmatically. 1059 file_name = forced_file_path_.AsUTF8Unsafe(); 1060 // Possibly has a 'download' attribute for the anchor. 1061 if (file_name.empty()) 1062 file_name = suggested_filename_; 1063 // From the URL file name. 1064 if (file_name.empty()) 1065 file_name = GetURL().ExtractFileName(); 1066 } 1067 1068 base::Callback<base::Value*(net::NetLog::LogLevel)> active_data = base::Bind( 1069 &ItemActivatedNetLogCallback, this, download_type, &file_name); 1070 if (active) { 1071 bound_net_log_.BeginEvent( 1072 net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data); 1073 } else { 1074 bound_net_log_.AddEvent( 1075 net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data); 1076 } 1077 1078 VLOG(20) << __FUNCTION__ << "() " << DebugString(true); 1079 } 1080 1081 // We're starting the download. 1082 void DownloadItemImpl::Start( 1083 scoped_ptr<DownloadFile> file, 1084 scoped_ptr<DownloadRequestHandleInterface> req_handle) { 1085 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1086 DCHECK(!download_file_.get()); 1087 DCHECK(file.get()); 1088 DCHECK(req_handle.get()); 1089 1090 download_file_ = file.Pass(); 1091 request_handle_ = req_handle.Pass(); 1092 1093 if (GetState() == CANCELLED) { 1094 // The download was in the process of resuming when it was cancelled. Don't 1095 // proceed. 1096 ReleaseDownloadFile(true); 1097 request_handle_->CancelRequest(); 1098 return; 1099 } 1100 1101 TransitionTo(IN_PROGRESS_INTERNAL, UPDATE_OBSERVERS); 1102 1103 BrowserThread::PostTask( 1104 BrowserThread::FILE, FROM_HERE, 1105 base::Bind(&DownloadFile::Initialize, 1106 // Safe because we control download file lifetime. 1107 base::Unretained(download_file_.get()), 1108 base::Bind(&DownloadItemImpl::OnDownloadFileInitialized, 1109 weak_ptr_factory_.GetWeakPtr()))); 1110 } 1111 1112 void DownloadItemImpl::OnDownloadFileInitialized( 1113 DownloadInterruptReason result) { 1114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1115 if (result != DOWNLOAD_INTERRUPT_REASON_NONE) { 1116 Interrupt(result); 1117 // TODO(rdsmith/asanka): Arguably we should show this in the UI, but 1118 // it's not at all clear what to show--we haven't done filename 1119 // determination, so we don't know what name to display. OTOH, 1120 // the failure mode of not showing the DI if the file initialization 1121 // fails isn't a good one. Can we hack up a name based on the 1122 // URLRequest? We'll need to make sure that initialization happens 1123 // properly. Possibly the right thing is to have the UI handle 1124 // this case specially. 1125 return; 1126 } 1127 1128 delegate_->DetermineDownloadTarget( 1129 this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined, 1130 weak_ptr_factory_.GetWeakPtr())); 1131 } 1132 1133 // Called by delegate_ when the download target path has been 1134 // determined. 1135 void DownloadItemImpl::OnDownloadTargetDetermined( 1136 const base::FilePath& target_path, 1137 TargetDisposition disposition, 1138 DownloadDangerType danger_type, 1139 const base::FilePath& intermediate_path) { 1140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1141 1142 // If the |target_path| is empty, then we consider this download to be 1143 // canceled. 1144 if (target_path.empty()) { 1145 Cancel(true); 1146 return; 1147 } 1148 1149 // TODO(rdsmith,asanka): We are ignoring the possibility that the download 1150 // has been interrupted at this point until we finish the intermediate 1151 // rename and set the full path. That's dangerous, because we might race 1152 // with resumption, either manual (because the interrupt is visible to the 1153 // UI) or automatic. If we keep the "ignore an error on download until file 1154 // name determination complete" semantics, we need to make sure that the 1155 // error is kept completely invisible until that point. 1156 1157 VLOG(20) << __FUNCTION__ << " " << target_path.value() << " " << disposition 1158 << " " << danger_type << " " << DebugString(true); 1159 1160 target_path_ = target_path; 1161 target_disposition_ = disposition; 1162 SetDangerType(danger_type); 1163 1164 // We want the intermediate and target paths to refer to the same directory so 1165 // that they are both on the same device and subject to same 1166 // space/permission/availability constraints. 1167 DCHECK(intermediate_path.DirName() == target_path.DirName()); 1168 1169 // During resumption, we may choose to proceed with the same intermediate 1170 // file. No rename is necessary if our intermediate file already has the 1171 // correct name. 1172 // 1173 // The intermediate name may change from its original value during filename 1174 // determination on resumption, for example if the reason for the interruption 1175 // was the download target running out space, resulting in a user prompt. 1176 if (intermediate_path == current_path_) { 1177 OnDownloadRenamedToIntermediateName(DOWNLOAD_INTERRUPT_REASON_NONE, 1178 intermediate_path); 1179 return; 1180 } 1181 1182 // Rename to intermediate name. 1183 // TODO(asanka): Skip this rename if AllDataSaved() is true. This avoids a 1184 // spurious rename when we can just rename to the final 1185 // filename. Unnecessary renames may cause bugs like 1186 // http://crbug.com/74187. 1187 DCHECK(!is_save_package_download_); 1188 DCHECK(download_file_.get()); 1189 DownloadFile::RenameCompletionCallback callback = 1190 base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName, 1191 weak_ptr_factory_.GetWeakPtr()); 1192 BrowserThread::PostTask( 1193 BrowserThread::FILE, FROM_HERE, 1194 base::Bind(&DownloadFile::RenameAndUniquify, 1195 // Safe because we control download file lifetime. 1196 base::Unretained(download_file_.get()), 1197 intermediate_path, callback)); 1198 } 1199 1200 void DownloadItemImpl::OnDownloadRenamedToIntermediateName( 1201 DownloadInterruptReason reason, 1202 const base::FilePath& full_path) { 1203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1204 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true); 1205 1206 if (DOWNLOAD_INTERRUPT_REASON_NONE != destination_error_) { 1207 // Process destination error. If both |reason| and |destination_error_| 1208 // refer to actual errors, we want to use the |destination_error_| as the 1209 // argument to the Interrupt() routine, as it happened first. 1210 if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) 1211 SetFullPath(full_path); 1212 Interrupt(destination_error_); 1213 destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE; 1214 } else if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) { 1215 Interrupt(reason); 1216 // All file errors result in file deletion above; no need to cleanup. The 1217 // current_path_ should be empty. Resuming this download will force a 1218 // restart and a re-doing of filename determination. 1219 DCHECK(current_path_.empty()); 1220 } else { 1221 SetFullPath(full_path); 1222 UpdateObservers(); 1223 MaybeCompleteDownload(); 1224 } 1225 } 1226 1227 // When SavePackage downloads MHTML to GData (see 1228 // SavePackageFilePickerChromeOS), GData calls MaybeCompleteDownload() like it 1229 // does for non-SavePackage downloads, but SavePackage downloads never satisfy 1230 // IsDownloadReadyForCompletion(). GDataDownloadObserver manually calls 1231 // DownloadItem::UpdateObservers() when the upload completes so that SavePackage 1232 // notices that the upload has completed and runs its normal Finish() pathway. 1233 // MaybeCompleteDownload() is never the mechanism by which SavePackage completes 1234 // downloads. SavePackage always uses its own Finish() to mark downloads 1235 // complete. 1236 void DownloadItemImpl::MaybeCompleteDownload() { 1237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1238 DCHECK(!is_save_package_download_); 1239 1240 if (!IsDownloadReadyForCompletion( 1241 base::Bind(&DownloadItemImpl::MaybeCompleteDownload, 1242 weak_ptr_factory_.GetWeakPtr()))) 1243 return; 1244 1245 // TODO(rdsmith): DCHECK that we only pass through this point 1246 // once per download. The natural way to do this is by a state 1247 // transition on the DownloadItem. 1248 1249 // Confirm we're in the proper set of states to be here; 1250 // have all data, have a history handle, (validated or safe). 1251 DCHECK_EQ(IN_PROGRESS_INTERNAL, state_); 1252 DCHECK(!IsDangerous()); 1253 DCHECK(all_data_saved_); 1254 1255 OnDownloadCompleting(); 1256 } 1257 1258 // Called by MaybeCompleteDownload() when it has determined that the download 1259 // is ready for completion. 1260 void DownloadItemImpl::OnDownloadCompleting() { 1261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1262 1263 if (state_ != IN_PROGRESS_INTERNAL) 1264 return; 1265 1266 VLOG(20) << __FUNCTION__ << "()" 1267 << " " << DebugString(true); 1268 DCHECK(!GetTargetFilePath().empty()); 1269 DCHECK(!IsDangerous()); 1270 1271 // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration. 1272 if (is_save_package_download_) { 1273 // Avoid doing anything on the file thread; there's nothing we control 1274 // there. 1275 // Strictly speaking, this skips giving the embedder a chance to open 1276 // the download. But on a save package download, there's no real 1277 // concept of opening. 1278 Completed(); 1279 return; 1280 } 1281 1282 DCHECK(download_file_.get()); 1283 // Unilaterally rename; even if it already has the right name, 1284 // we need theannotation. 1285 DownloadFile::RenameCompletionCallback callback = 1286 base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName, 1287 weak_ptr_factory_.GetWeakPtr()); 1288 BrowserThread::PostTask( 1289 BrowserThread::FILE, FROM_HERE, 1290 base::Bind(&DownloadFile::RenameAndAnnotate, 1291 base::Unretained(download_file_.get()), 1292 GetTargetFilePath(), callback)); 1293 } 1294 1295 void DownloadItemImpl::OnDownloadRenamedToFinalName( 1296 DownloadInterruptReason reason, 1297 const base::FilePath& full_path) { 1298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1299 DCHECK(!is_save_package_download_); 1300 1301 // If a cancel or interrupt hit, we'll cancel the DownloadFile, which 1302 // will result in deleting the file on the file thread. So we don't 1303 // care about the name having been changed. 1304 if (state_ != IN_PROGRESS_INTERNAL) 1305 return; 1306 1307 VLOG(20) << __FUNCTION__ << "()" 1308 << " full_path = \"" << full_path.value() << "\"" 1309 << " " << DebugString(false); 1310 1311 if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) { 1312 Interrupt(reason); 1313 1314 // All file errors should have resulted in in file deletion above. On 1315 // resumption we will need to re-do filename determination. 1316 DCHECK(current_path_.empty()); 1317 return; 1318 } 1319 1320 DCHECK(target_path_ == full_path); 1321 1322 if (full_path != current_path_) { 1323 // full_path is now the current and target file path. 1324 DCHECK(!full_path.empty()); 1325 SetFullPath(full_path); 1326 } 1327 1328 // Complete the download and release the DownloadFile. 1329 DCHECK(download_file_.get()); 1330 ReleaseDownloadFile(false); 1331 1332 // We're not completely done with the download item yet, but at this 1333 // point we're committed to complete the download. Cancels (or Interrupts, 1334 // though it's not clear how they could happen) after this point will be 1335 // ignored. 1336 TransitionTo(COMPLETING_INTERNAL, DONT_UPDATE_OBSERVERS); 1337 1338 if (delegate_->ShouldOpenDownload( 1339 this, base::Bind(&DownloadItemImpl::DelayedDownloadOpened, 1340 weak_ptr_factory_.GetWeakPtr()))) { 1341 Completed(); 1342 } else { 1343 delegate_delayed_complete_ = true; 1344 UpdateObservers(); 1345 } 1346 } 1347 1348 void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened) { 1349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1350 1351 auto_opened_ = auto_opened; 1352 Completed(); 1353 } 1354 1355 void DownloadItemImpl::Completed() { 1356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1357 1358 VLOG(20) << __FUNCTION__ << "() " << DebugString(false); 1359 1360 DCHECK(all_data_saved_); 1361 end_time_ = base::Time::Now(); 1362 TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS); 1363 RecordDownloadCompleted(start_tick_, received_bytes_); 1364 1365 if (auto_opened_) { 1366 // If it was already handled by the delegate, do nothing. 1367 } else if (GetOpenWhenComplete() || 1368 ShouldOpenFileBasedOnExtension() || 1369 IsTemporary()) { 1370 // If the download is temporary, like in drag-and-drop, do not open it but 1371 // we still need to set it auto-opened so that it can be removed from the 1372 // download shelf. 1373 if (!IsTemporary()) 1374 OpenDownload(); 1375 1376 auto_opened_ = true; 1377 UpdateObservers(); 1378 } 1379 } 1380 1381 void DownloadItemImpl::OnResumeRequestStarted(DownloadItem* item, 1382 net::Error error) { 1383 // If |item| is not NULL, then Start() has been called already, and nothing 1384 // more needs to be done here. 1385 if (item) { 1386 DCHECK_EQ(net::OK, error); 1387 DCHECK_EQ(static_cast<DownloadItem*>(this), item); 1388 return; 1389 } 1390 // Otherwise, the request failed without passing through 1391 // DownloadResourceHandler::OnResponseStarted. 1392 if (error == net::OK) 1393 error = net::ERR_FAILED; 1394 DownloadInterruptReason reason = 1395 ConvertNetErrorToInterruptReason(error, DOWNLOAD_INTERRUPT_FROM_NETWORK); 1396 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, reason); 1397 Interrupt(reason); 1398 } 1399 1400 // **** End of Download progression cascade 1401 1402 // An error occurred somewhere. 1403 void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) { 1404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1405 1406 // Somewhat counter-intuitively, it is possible for us to receive an 1407 // interrupt after we've already been interrupted. The generation of 1408 // interrupts from the file thread Renames and the generation of 1409 // interrupts from disk writes go through two different mechanisms (driven 1410 // by rename requests from UI thread and by write requests from IO thread, 1411 // respectively), and since we choose not to keep state on the File thread, 1412 // this is the place where the races collide. It's also possible for 1413 // interrupts to race with cancels. 1414 1415 // Whatever happens, the first one to hit the UI thread wins. 1416 if (state_ != IN_PROGRESS_INTERNAL && state_ != RESUMING_INTERNAL) 1417 return; 1418 1419 last_reason_ = reason; 1420 1421 ResumeMode resume_mode = GetResumeMode(); 1422 1423 if (state_ == IN_PROGRESS_INTERNAL) { 1424 // Cancel (delete file) if we're going to restart; no point in leaving 1425 // data around we aren't going to use. Also cancel if resumption isn't 1426 // enabled for the same reason. 1427 ReleaseDownloadFile(resume_mode == RESUME_MODE_IMMEDIATE_RESTART || 1428 resume_mode == RESUME_MODE_USER_RESTART || 1429 !IsDownloadResumptionEnabled()); 1430 1431 // Cancel the originating URL request. 1432 request_handle_->CancelRequest(); 1433 } else { 1434 DCHECK(!download_file_.get()); 1435 } 1436 1437 // Reset all data saved, as even if we did save all the data we're going 1438 // to go through another round of downloading when we resume. 1439 // There's a potential problem here in the abstract, as if we did download 1440 // all the data and then run into a continuable error, on resumption we 1441 // won't download any more data. However, a) there are currently no 1442 // continuable errors that can occur after we download all the data, and 1443 // b) if there were, that would probably simply result in a null range 1444 // request, which would generate a DestinationCompleted() notification 1445 // from the DownloadFile, which would behave properly with setting 1446 // all_data_saved_ to false here. 1447 all_data_saved_ = false; 1448 1449 TransitionTo(INTERRUPTED_INTERNAL, DONT_UPDATE_OBSERVERS); 1450 RecordDownloadInterrupted(reason, received_bytes_, total_bytes_); 1451 if (!GetWebContents()) 1452 RecordDownloadCount(INTERRUPTED_WITHOUT_WEBCONTENTS); 1453 1454 AutoResumeIfValid(); 1455 UpdateObservers(); 1456 } 1457 1458 void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file) { 1459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1460 1461 if (destroy_file) { 1462 BrowserThread::PostTask( 1463 BrowserThread::FILE, FROM_HERE, 1464 // Will be deleted at end of task execution. 1465 base::Bind(&DownloadFileCancel, base::Passed(&download_file_))); 1466 // Avoid attempting to reuse the intermediate file by clearing out 1467 // current_path_. 1468 current_path_.clear(); 1469 } else { 1470 BrowserThread::PostTask( 1471 BrowserThread::FILE, 1472 FROM_HERE, 1473 base::Bind(base::IgnoreResult(&DownloadFileDetach), 1474 // Will be deleted at end of task execution. 1475 base::Passed(&download_file_))); 1476 } 1477 // Don't accept any more messages from the DownloadFile, and null 1478 // out any previous "all data received". This also breaks links to 1479 // other entities we've given out weak pointers to. 1480 weak_ptr_factory_.InvalidateWeakPtrs(); 1481 } 1482 1483 bool DownloadItemImpl::IsDownloadReadyForCompletion( 1484 const base::Closure& state_change_notification) { 1485 // If we don't have all the data, the download is not ready for 1486 // completion. 1487 if (!AllDataSaved()) 1488 return false; 1489 1490 // If the download is dangerous, but not yet validated, it's not ready for 1491 // completion. 1492 if (IsDangerous()) 1493 return false; 1494 1495 // If the download isn't active (e.g. has been cancelled) it's not 1496 // ready for completion. 1497 if (state_ != IN_PROGRESS_INTERNAL) 1498 return false; 1499 1500 // If the target filename hasn't been determined, then it's not ready for 1501 // completion. This is checked in ReadyForDownloadCompletionDone(). 1502 if (GetTargetFilePath().empty()) 1503 return false; 1504 1505 // This is checked in NeedsRename(). Without this conditional, 1506 // browser_tests:DownloadTest.DownloadMimeType fails the DCHECK. 1507 if (target_path_.DirName() != current_path_.DirName()) 1508 return false; 1509 1510 // Give the delegate a chance to hold up a stop sign. It'll call 1511 // use back through the passed callback if it does and that state changes. 1512 if (!delegate_->ShouldCompleteDownload(this, state_change_notification)) 1513 return false; 1514 1515 return true; 1516 } 1517 1518 void DownloadItemImpl::TransitionTo(DownloadInternalState new_state, 1519 ShouldUpdateObservers notify_action) { 1520 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1521 1522 if (state_ == new_state) 1523 return; 1524 1525 DownloadInternalState old_state = state_; 1526 state_ = new_state; 1527 1528 switch (state_) { 1529 case COMPLETING_INTERNAL: 1530 bound_net_log_.AddEvent( 1531 net::NetLog::TYPE_DOWNLOAD_ITEM_COMPLETING, 1532 base::Bind(&ItemCompletingNetLogCallback, received_bytes_, &hash_)); 1533 break; 1534 case COMPLETE_INTERNAL: 1535 bound_net_log_.AddEvent( 1536 net::NetLog::TYPE_DOWNLOAD_ITEM_FINISHED, 1537 base::Bind(&ItemFinishedNetLogCallback, auto_opened_)); 1538 break; 1539 case INTERRUPTED_INTERNAL: 1540 bound_net_log_.AddEvent( 1541 net::NetLog::TYPE_DOWNLOAD_ITEM_INTERRUPTED, 1542 base::Bind(&ItemInterruptedNetLogCallback, last_reason_, 1543 received_bytes_, &hash_state_)); 1544 break; 1545 case IN_PROGRESS_INTERNAL: 1546 if (old_state == INTERRUPTED_INTERNAL) { 1547 bound_net_log_.AddEvent( 1548 net::NetLog::TYPE_DOWNLOAD_ITEM_RESUMED, 1549 base::Bind(&ItemResumingNetLogCallback, 1550 false, last_reason_, received_bytes_, &hash_state_)); 1551 } 1552 break; 1553 case CANCELLED_INTERNAL: 1554 bound_net_log_.AddEvent( 1555 net::NetLog::TYPE_DOWNLOAD_ITEM_CANCELED, 1556 base::Bind(&ItemCanceledNetLogCallback, received_bytes_, 1557 &hash_state_)); 1558 break; 1559 default: 1560 break; 1561 } 1562 1563 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true) 1564 << " " << InternalToExternalState(old_state) 1565 << " " << InternalToExternalState(state_); 1566 1567 bool is_done = (state_ != IN_PROGRESS_INTERNAL && 1568 state_ != COMPLETING_INTERNAL); 1569 bool was_done = (old_state != IN_PROGRESS_INTERNAL && 1570 old_state != COMPLETING_INTERNAL); 1571 // Termination 1572 if (is_done && !was_done) 1573 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE); 1574 1575 // Resumption 1576 if (was_done && !is_done) { 1577 std::string file_name(target_path_.BaseName().AsUTF8Unsafe()); 1578 bound_net_log_.BeginEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, 1579 base::Bind(&ItemActivatedNetLogCallback, 1580 this, SRC_ACTIVE_DOWNLOAD, 1581 &file_name)); 1582 } 1583 1584 if (notify_action == UPDATE_OBSERVERS) 1585 UpdateObservers(); 1586 } 1587 1588 void DownloadItemImpl::SetDangerType(DownloadDangerType danger_type) { 1589 if (danger_type != danger_type_) { 1590 bound_net_log_.AddEvent( 1591 net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED, 1592 base::Bind(&ItemCheckedNetLogCallback, danger_type)); 1593 } 1594 // Only record the Malicious UMA stat if it's going from {not malicious} -> 1595 // {malicious}. 1596 if ((danger_type_ == DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS || 1597 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE || 1598 danger_type_ == DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT || 1599 danger_type_ == DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT) && 1600 (danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST || 1601 danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL || 1602 danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT || 1603 danger_type == DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED)) { 1604 RecordMaliciousDownloadClassified(danger_type); 1605 } 1606 danger_type_ = danger_type; 1607 } 1608 1609 void DownloadItemImpl::SetFullPath(const base::FilePath& new_path) { 1610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1611 VLOG(20) << __FUNCTION__ << "()" 1612 << " new_path = \"" << new_path.value() << "\"" 1613 << " " << DebugString(true); 1614 DCHECK(!new_path.empty()); 1615 1616 bound_net_log_.AddEvent( 1617 net::NetLog::TYPE_DOWNLOAD_ITEM_RENAMED, 1618 base::Bind(&ItemRenamedNetLogCallback, ¤t_path_, &new_path)); 1619 1620 current_path_ = new_path; 1621 } 1622 1623 void DownloadItemImpl::AutoResumeIfValid() { 1624 DVLOG(20) << __FUNCTION__ << "() " << DebugString(true); 1625 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1626 ResumeMode mode = GetResumeMode(); 1627 1628 if (mode != RESUME_MODE_IMMEDIATE_RESTART && 1629 mode != RESUME_MODE_IMMEDIATE_CONTINUE) { 1630 return; 1631 } 1632 1633 auto_resume_count_++; 1634 1635 ResumeInterruptedDownload(); 1636 } 1637 1638 void DownloadItemImpl::ResumeInterruptedDownload() { 1639 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1640 1641 // If the flag for downloads resumption isn't enabled, ignore 1642 // this request. 1643 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 1644 if (!command_line.HasSwitch(switches::kEnableDownloadResumption)) 1645 return; 1646 1647 // If we're not interrupted, ignore the request; our caller is drunk. 1648 if (state_ != INTERRUPTED_INTERNAL) 1649 return; 1650 1651 // If we can't get a web contents, we can't resume the download. 1652 // TODO(rdsmith): Find some alternative web contents to use--this 1653 // means we can't restart a download if it's a download imported 1654 // from the history. 1655 if (!GetWebContents()) 1656 return; 1657 1658 // Reset the appropriate state if restarting. 1659 ResumeMode mode = GetResumeMode(); 1660 if (mode == RESUME_MODE_IMMEDIATE_RESTART || 1661 mode == RESUME_MODE_USER_RESTART) { 1662 received_bytes_ = 0; 1663 hash_state_ = ""; 1664 last_modified_time_ = ""; 1665 etag_ = ""; 1666 } 1667 1668 scoped_ptr<DownloadUrlParameters> download_params( 1669 DownloadUrlParameters::FromWebContents(GetWebContents(), 1670 GetOriginalUrl())); 1671 1672 download_params->set_file_path(GetFullPath()); 1673 download_params->set_offset(GetReceivedBytes()); 1674 download_params->set_hash_state(GetHashState()); 1675 download_params->set_last_modified(GetLastModifiedTime()); 1676 download_params->set_etag(GetETag()); 1677 download_params->set_callback( 1678 base::Bind(&DownloadItemImpl::OnResumeRequestStarted, 1679 weak_ptr_factory_.GetWeakPtr())); 1680 1681 delegate_->ResumeInterruptedDownload(download_params.Pass(), GetId()); 1682 // Just in case we were interrupted while paused. 1683 is_paused_ = false; 1684 1685 TransitionTo(RESUMING_INTERNAL, DONT_UPDATE_OBSERVERS); 1686 } 1687 1688 // static 1689 DownloadItem::DownloadState DownloadItemImpl::InternalToExternalState( 1690 DownloadInternalState internal_state) { 1691 switch (internal_state) { 1692 case IN_PROGRESS_INTERNAL: 1693 return IN_PROGRESS; 1694 case COMPLETING_INTERNAL: 1695 return IN_PROGRESS; 1696 case COMPLETE_INTERNAL: 1697 return COMPLETE; 1698 case CANCELLED_INTERNAL: 1699 return CANCELLED; 1700 case INTERRUPTED_INTERNAL: 1701 return INTERRUPTED; 1702 case RESUMING_INTERNAL: 1703 return INTERRUPTED; 1704 case MAX_DOWNLOAD_INTERNAL_STATE: 1705 break; 1706 } 1707 NOTREACHED(); 1708 return MAX_DOWNLOAD_STATE; 1709 } 1710 1711 // static 1712 DownloadItemImpl::DownloadInternalState 1713 DownloadItemImpl::ExternalToInternalState( 1714 DownloadState external_state) { 1715 switch (external_state) { 1716 case IN_PROGRESS: 1717 return IN_PROGRESS_INTERNAL; 1718 case COMPLETE: 1719 return COMPLETE_INTERNAL; 1720 case CANCELLED: 1721 return CANCELLED_INTERNAL; 1722 case INTERRUPTED: 1723 return INTERRUPTED_INTERNAL; 1724 default: 1725 NOTREACHED(); 1726 } 1727 return MAX_DOWNLOAD_INTERNAL_STATE; 1728 } 1729 1730 const char* DownloadItemImpl::DebugDownloadStateString( 1731 DownloadInternalState state) { 1732 switch (state) { 1733 case IN_PROGRESS_INTERNAL: 1734 return "IN_PROGRESS"; 1735 case COMPLETING_INTERNAL: 1736 return "COMPLETING"; 1737 case COMPLETE_INTERNAL: 1738 return "COMPLETE"; 1739 case CANCELLED_INTERNAL: 1740 return "CANCELLED"; 1741 case INTERRUPTED_INTERNAL: 1742 return "INTERRUPTED"; 1743 case RESUMING_INTERNAL: 1744 return "RESUMING"; 1745 case MAX_DOWNLOAD_INTERNAL_STATE: 1746 break; 1747 }; 1748 NOTREACHED() << "Unknown download state " << state; 1749 return "unknown"; 1750 } 1751 1752 const char* DownloadItemImpl::DebugResumeModeString(ResumeMode mode) { 1753 switch (mode) { 1754 case RESUME_MODE_INVALID: 1755 return "INVALID"; 1756 case RESUME_MODE_IMMEDIATE_CONTINUE: 1757 return "IMMEDIATE_CONTINUE"; 1758 case RESUME_MODE_IMMEDIATE_RESTART: 1759 return "IMMEDIATE_RESTART"; 1760 case RESUME_MODE_USER_CONTINUE: 1761 return "USER_CONTINUE"; 1762 case RESUME_MODE_USER_RESTART: 1763 return "USER_RESTART"; 1764 } 1765 NOTREACHED() << "Unknown resume mode " << mode; 1766 return "unknown"; 1767 } 1768 1769 } // namespace content 1770