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