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