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