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