1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // File method ordering: Methods in this file are in the same order as 6 // in download_item_impl.h, with the following exception: The public 7 // interface Start is placed in chronological order with the other 8 // (private) routines that together define a DownloadItem's state 9 // transitions as the download progresses. See "Download progression 10 // cascade" later in this file. 11 12 // A regular DownloadItem (created for a download in this session of the 13 // browser) normally goes through the following states: 14 // * Created (when download starts) 15 // * Destination filename determined 16 // * Entered into the history database. 17 // * Made visible in the download shelf. 18 // * All the data is saved. Note that the actual data download occurs 19 // in parallel with the above steps, but until those steps are 20 // complete, the state of the data save will be ignored. 21 // * Download file is renamed to its final name, and possibly 22 // auto-opened. 23 24 #include "content/browser/download/download_item_impl.h" 25 26 #include <vector> 27 28 #include "base/basictypes.h" 29 #include "base/bind.h" 30 #include "base/command_line.h" 31 #include "base/file_util.h" 32 #include "base/format_macros.h" 33 #include "base/logging.h" 34 #include "base/metrics/histogram.h" 35 #include "base/stl_util.h" 36 #include "base/strings/stringprintf.h" 37 #include "base/strings/utf_string_conversions.h" 38 #include "content/browser/download/download_create_info.h" 39 #include "content/browser/download/download_file.h" 40 #include "content/browser/download/download_interrupt_reasons_impl.h" 41 #include "content/browser/download/download_item_impl_delegate.h" 42 #include "content/browser/download/download_request_handle.h" 43 #include "content/browser/download/download_stats.h" 44 #include "content/browser/renderer_host/render_view_host_impl.h" 45 #include "content/browser/web_contents/web_contents_impl.h" 46 #include "content/public/browser/browser_context.h" 47 #include "content/public/browser/browser_thread.h" 48 #include "content/public/browser/content_browser_client.h" 49 #include "content/public/browser/download_danger_type.h" 50 #include "content/public/browser/download_interrupt_reasons.h" 51 #include "content/public/browser/download_url_parameters.h" 52 #include "content/public/common/content_switches.h" 53 #include "content/public/common/referrer.h" 54 #include "net/base/net_util.h" 55 56 namespace content { 57 58 namespace { 59 60 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 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_(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_(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 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 mode = RESUME_MODE_INVALID; 931 break; 932 } 933 934 return mode; 935 } 936 937 void DownloadItemImpl::MergeOriginInfoOnResume( 938 const DownloadCreateInfo& new_create_info) { 939 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 940 DCHECK_EQ(RESUMING_INTERNAL, state_); 941 DCHECK(!new_create_info.url_chain.empty()); 942 943 // We are going to tack on any new redirects to our list of redirects. 944 // When a download is resumed, the URL used for the resumption request is the 945 // one at the end of the previous redirect chain. Tacking additional redirects 946 // to the end of this chain ensures that: 947 // - If the download needs to be resumed again, the ETag/Last-Modified headers 948 // will be used with the last server that sent them to us. 949 // - The redirect chain contains all the servers that were involved in this 950 // download since the initial request, in order. 951 std::vector<GURL>::const_iterator chain_iter = 952 new_create_info.url_chain.begin(); 953 if (*chain_iter == url_chain_.back()) 954 ++chain_iter; 955 956 // Record some stats. If the precondition failed (the server returned 957 // HTTP_PRECONDITION_FAILED), then the download will automatically retried as 958 // a full request rather than a partial. Full restarts clobber validators. 959 int origin_state = 0; 960 if (chain_iter != new_create_info.url_chain.end()) 961 origin_state |= ORIGIN_STATE_ON_RESUMPTION_ADDITIONAL_REDIRECTS; 962 if (etag_ != new_create_info.etag || 963 last_modified_time_ != new_create_info.last_modified) 964 origin_state |= ORIGIN_STATE_ON_RESUMPTION_VALIDATORS_CHANGED; 965 if (content_disposition_ != new_create_info.content_disposition) 966 origin_state |= ORIGIN_STATE_ON_RESUMPTION_CONTENT_DISPOSITION_CHANGED; 967 RecordOriginStateOnResumption(new_create_info.save_info->offset != 0, 968 origin_state); 969 970 url_chain_.insert( 971 url_chain_.end(), chain_iter, new_create_info.url_chain.end()); 972 etag_ = new_create_info.etag; 973 last_modified_time_ = new_create_info.last_modified; 974 content_disposition_ = new_create_info.content_disposition; 975 976 // Don't update observers. This method is expected to be called just before a 977 // DownloadFile is created and Start() is called. The observers will be 978 // notified when the download transitions to the IN_PROGRESS state. 979 } 980 981 void DownloadItemImpl::NotifyRemoved() { 982 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadRemoved(this)); 983 } 984 985 void DownloadItemImpl::OnDownloadedFileRemoved() { 986 file_externally_removed_ = true; 987 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true); 988 UpdateObservers(); 989 } 990 991 base::WeakPtr<DownloadDestinationObserver> 992 DownloadItemImpl::DestinationObserverAsWeakPtr() { 993 return weak_ptr_factory_.GetWeakPtr(); 994 } 995 996 const net::BoundNetLog& DownloadItemImpl::GetBoundNetLog() const { 997 return bound_net_log_; 998 } 999 1000 void DownloadItemImpl::SetTotalBytes(int64 total_bytes) { 1001 total_bytes_ = total_bytes; 1002 } 1003 1004 void DownloadItemImpl::OnAllDataSaved(const std::string& final_hash) { 1005 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1006 1007 DCHECK_EQ(IN_PROGRESS_INTERNAL, state_); 1008 DCHECK(!all_data_saved_); 1009 all_data_saved_ = true; 1010 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true); 1011 1012 // Store final hash and null out intermediate serialized hash state. 1013 hash_ = final_hash; 1014 hash_state_ = ""; 1015 1016 UpdateObservers(); 1017 } 1018 1019 void DownloadItemImpl::MarkAsComplete() { 1020 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1021 1022 DCHECK(all_data_saved_); 1023 end_time_ = base::Time::Now(); 1024 TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS); 1025 } 1026 1027 void DownloadItemImpl::DestinationUpdate(int64 bytes_so_far, 1028 int64 bytes_per_sec, 1029 const std::string& hash_state) { 1030 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1031 VLOG(20) << __FUNCTION__ << " so_far=" << bytes_so_far 1032 << " per_sec=" << bytes_per_sec << " download=" << DebugString(true); 1033 1034 if (GetState() != IN_PROGRESS) { 1035 // Ignore if we're no longer in-progress. This can happen if we race a 1036 // Cancel on the UI thread with an update on the FILE thread. 1037 // 1038 // TODO(rdsmith): Arguably we should let this go through, as this means 1039 // the download really did get further than we know before it was 1040 // cancelled. But the gain isn't very large, and the code is more 1041 // fragile if it has to support in progress updates in a non-in-progress 1042 // state. This issue should be readdressed when we revamp performance 1043 // reporting. 1044 return; 1045 } 1046 bytes_per_sec_ = bytes_per_sec; 1047 hash_state_ = hash_state; 1048 received_bytes_ = bytes_so_far; 1049 1050 // If we've received more data than we were expecting (bad server info?), 1051 // revert to 'unknown size mode'. 1052 if (received_bytes_ > total_bytes_) 1053 total_bytes_ = 0; 1054 1055 if (bound_net_log_.IsLogging()) { 1056 bound_net_log_.AddEvent( 1057 net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED, 1058 net::NetLog::Int64Callback("bytes_so_far", received_bytes_)); 1059 } 1060 1061 UpdateObservers(); 1062 } 1063 1064 void DownloadItemImpl::DestinationError(DownloadInterruptReason reason) { 1065 // Postpone recognition of this error until after file name determination 1066 // has completed and the intermediate file has been renamed to simplify 1067 // resumption conditions. 1068 if (current_path_.empty() || target_path_.empty()) 1069 destination_error_ = reason; 1070 else 1071 Interrupt(reason); 1072 } 1073 1074 void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) { 1075 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true); 1076 if (GetState() != IN_PROGRESS) 1077 return; 1078 OnAllDataSaved(final_hash); 1079 MaybeCompleteDownload(); 1080 } 1081 1082 // **** Download progression cascade 1083 1084 void DownloadItemImpl::Init(bool active, 1085 DownloadType download_type) { 1086 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1087 1088 if (active) 1089 RecordDownloadCount(START_COUNT); 1090 1091 std::string file_name; 1092 if (download_type == SRC_HISTORY_IMPORT) { 1093 // target_path_ works for History and Save As versions. 1094 file_name = target_path_.AsUTF8Unsafe(); 1095 } else { 1096 // See if it's set programmatically. 1097 file_name = forced_file_path_.AsUTF8Unsafe(); 1098 // Possibly has a 'download' attribute for the anchor. 1099 if (file_name.empty()) 1100 file_name = suggested_filename_; 1101 // From the URL file name. 1102 if (file_name.empty()) 1103 file_name = GetURL().ExtractFileName(); 1104 } 1105 1106 base::Callback<base::Value*(net::NetLog::LogLevel)> active_data = base::Bind( 1107 &ItemActivatedNetLogCallback, this, download_type, &file_name); 1108 if (active) { 1109 bound_net_log_.BeginEvent( 1110 net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data); 1111 } else { 1112 bound_net_log_.AddEvent( 1113 net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data); 1114 } 1115 1116 VLOG(20) << __FUNCTION__ << "() " << DebugString(true); 1117 } 1118 1119 // We're starting the download. 1120 void DownloadItemImpl::Start( 1121 scoped_ptr<DownloadFile> file, 1122 scoped_ptr<DownloadRequestHandleInterface> req_handle) { 1123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1124 DCHECK(!download_file_.get()); 1125 DCHECK(file.get()); 1126 DCHECK(req_handle.get()); 1127 1128 download_file_ = file.Pass(); 1129 request_handle_ = req_handle.Pass(); 1130 1131 if (GetState() == CANCELLED) { 1132 // The download was in the process of resuming when it was cancelled. Don't 1133 // proceed. 1134 ReleaseDownloadFile(true); 1135 request_handle_->CancelRequest(); 1136 return; 1137 } 1138 1139 TransitionTo(IN_PROGRESS_INTERNAL, UPDATE_OBSERVERS); 1140 1141 BrowserThread::PostTask( 1142 BrowserThread::FILE, FROM_HERE, 1143 base::Bind(&DownloadFile::Initialize, 1144 // Safe because we control download file lifetime. 1145 base::Unretained(download_file_.get()), 1146 base::Bind(&DownloadItemImpl::OnDownloadFileInitialized, 1147 weak_ptr_factory_.GetWeakPtr()))); 1148 } 1149 1150 void DownloadItemImpl::OnDownloadFileInitialized( 1151 DownloadInterruptReason result) { 1152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1153 if (result != DOWNLOAD_INTERRUPT_REASON_NONE) { 1154 Interrupt(result); 1155 // TODO(rdsmith/asanka): Arguably we should show this in the UI, but 1156 // it's not at all clear what to show--we haven't done filename 1157 // determination, so we don't know what name to display. OTOH, 1158 // the failure mode of not showing the DI if the file initialization 1159 // fails isn't a good one. Can we hack up a name based on the 1160 // URLRequest? We'll need to make sure that initialization happens 1161 // properly. Possibly the right thing is to have the UI handle 1162 // this case specially. 1163 return; 1164 } 1165 1166 delegate_->DetermineDownloadTarget( 1167 this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined, 1168 weak_ptr_factory_.GetWeakPtr())); 1169 } 1170 1171 // Called by delegate_ when the download target path has been 1172 // determined. 1173 void DownloadItemImpl::OnDownloadTargetDetermined( 1174 const base::FilePath& target_path, 1175 TargetDisposition disposition, 1176 DownloadDangerType danger_type, 1177 const base::FilePath& intermediate_path) { 1178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1179 1180 // If the |target_path| is empty, then we consider this download to be 1181 // canceled. 1182 if (target_path.empty()) { 1183 Cancel(true); 1184 return; 1185 } 1186 1187 // TODO(rdsmith,asanka): We are ignoring the possibility that the download 1188 // has been interrupted at this point until we finish the intermediate 1189 // rename and set the full path. That's dangerous, because we might race 1190 // with resumption, either manual (because the interrupt is visible to the 1191 // UI) or automatic. If we keep the "ignore an error on download until file 1192 // name determination complete" semantics, we need to make sure that the 1193 // error is kept completely invisible until that point. 1194 1195 VLOG(20) << __FUNCTION__ << " " << target_path.value() << " " << disposition 1196 << " " << danger_type << " " << DebugString(true); 1197 1198 target_path_ = target_path; 1199 target_disposition_ = disposition; 1200 SetDangerType(danger_type); 1201 1202 // We want the intermediate and target paths to refer to the same directory so 1203 // that they are both on the same device and subject to same 1204 // space/permission/availability constraints. 1205 DCHECK(intermediate_path.DirName() == target_path.DirName()); 1206 1207 // During resumption, we may choose to proceed with the same intermediate 1208 // file. No rename is necessary if our intermediate file already has the 1209 // correct name. 1210 // 1211 // The intermediate name may change from its original value during filename 1212 // determination on resumption, for example if the reason for the interruption 1213 // was the download target running out space, resulting in a user prompt. 1214 if (intermediate_path == current_path_) { 1215 OnDownloadRenamedToIntermediateName(DOWNLOAD_INTERRUPT_REASON_NONE, 1216 intermediate_path); 1217 return; 1218 } 1219 1220 // Rename to intermediate name. 1221 // TODO(asanka): Skip this rename if AllDataSaved() is true. This avoids a 1222 // spurious rename when we can just rename to the final 1223 // filename. Unnecessary renames may cause bugs like 1224 // http://crbug.com/74187. 1225 DCHECK(!is_save_package_download_); 1226 DCHECK(download_file_.get()); 1227 DownloadFile::RenameCompletionCallback callback = 1228 base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName, 1229 weak_ptr_factory_.GetWeakPtr()); 1230 BrowserThread::PostTask( 1231 BrowserThread::FILE, FROM_HERE, 1232 base::Bind(&DownloadFile::RenameAndUniquify, 1233 // Safe because we control download file lifetime. 1234 base::Unretained(download_file_.get()), 1235 intermediate_path, callback)); 1236 } 1237 1238 void DownloadItemImpl::OnDownloadRenamedToIntermediateName( 1239 DownloadInterruptReason reason, 1240 const base::FilePath& full_path) { 1241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1242 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true); 1243 1244 if (DOWNLOAD_INTERRUPT_REASON_NONE != destination_error_) { 1245 // Process destination error. If both |reason| and |destination_error_| 1246 // refer to actual errors, we want to use the |destination_error_| as the 1247 // argument to the Interrupt() routine, as it happened first. 1248 if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) 1249 SetFullPath(full_path); 1250 Interrupt(destination_error_); 1251 destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE; 1252 } else if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) { 1253 Interrupt(reason); 1254 // All file errors result in file deletion above; no need to cleanup. The 1255 // current_path_ should be empty. Resuming this download will force a 1256 // restart and a re-doing of filename determination. 1257 DCHECK(current_path_.empty()); 1258 } else { 1259 SetFullPath(full_path); 1260 UpdateObservers(); 1261 MaybeCompleteDownload(); 1262 } 1263 } 1264 1265 // When SavePackage downloads MHTML to GData (see 1266 // SavePackageFilePickerChromeOS), GData calls MaybeCompleteDownload() like it 1267 // does for non-SavePackage downloads, but SavePackage downloads never satisfy 1268 // IsDownloadReadyForCompletion(). GDataDownloadObserver manually calls 1269 // DownloadItem::UpdateObservers() when the upload completes so that SavePackage 1270 // notices that the upload has completed and runs its normal Finish() pathway. 1271 // MaybeCompleteDownload() is never the mechanism by which SavePackage completes 1272 // downloads. SavePackage always uses its own Finish() to mark downloads 1273 // complete. 1274 void DownloadItemImpl::MaybeCompleteDownload() { 1275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1276 DCHECK(!is_save_package_download_); 1277 1278 if (!IsDownloadReadyForCompletion( 1279 base::Bind(&DownloadItemImpl::MaybeCompleteDownload, 1280 weak_ptr_factory_.GetWeakPtr()))) 1281 return; 1282 1283 // TODO(rdsmith): DCHECK that we only pass through this point 1284 // once per download. The natural way to do this is by a state 1285 // transition on the DownloadItem. 1286 1287 // Confirm we're in the proper set of states to be here; 1288 // have all data, have a history handle, (validated or safe). 1289 DCHECK_EQ(IN_PROGRESS_INTERNAL, state_); 1290 DCHECK(!IsDangerous()); 1291 DCHECK(all_data_saved_); 1292 1293 OnDownloadCompleting(); 1294 } 1295 1296 // Called by MaybeCompleteDownload() when it has determined that the download 1297 // is ready for completion. 1298 void DownloadItemImpl::OnDownloadCompleting() { 1299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1300 1301 if (state_ != IN_PROGRESS_INTERNAL) 1302 return; 1303 1304 VLOG(20) << __FUNCTION__ << "()" 1305 << " " << DebugString(true); 1306 DCHECK(!GetTargetFilePath().empty()); 1307 DCHECK(!IsDangerous()); 1308 1309 // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration. 1310 if (is_save_package_download_) { 1311 // Avoid doing anything on the file thread; there's nothing we control 1312 // there. 1313 // Strictly speaking, this skips giving the embedder a chance to open 1314 // the download. But on a save package download, there's no real 1315 // concept of opening. 1316 Completed(); 1317 return; 1318 } 1319 1320 DCHECK(download_file_.get()); 1321 // Unilaterally rename; even if it already has the right name, 1322 // we need theannotation. 1323 DownloadFile::RenameCompletionCallback callback = 1324 base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName, 1325 weak_ptr_factory_.GetWeakPtr()); 1326 BrowserThread::PostTask( 1327 BrowserThread::FILE, FROM_HERE, 1328 base::Bind(&DownloadFile::RenameAndAnnotate, 1329 base::Unretained(download_file_.get()), 1330 GetTargetFilePath(), callback)); 1331 } 1332 1333 void DownloadItemImpl::OnDownloadRenamedToFinalName( 1334 DownloadInterruptReason reason, 1335 const base::FilePath& full_path) { 1336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1337 DCHECK(!is_save_package_download_); 1338 1339 // If a cancel or interrupt hit, we'll cancel the DownloadFile, which 1340 // will result in deleting the file on the file thread. So we don't 1341 // care about the name having been changed. 1342 if (state_ != IN_PROGRESS_INTERNAL) 1343 return; 1344 1345 VLOG(20) << __FUNCTION__ << "()" 1346 << " full_path = \"" << full_path.value() << "\"" 1347 << " " << DebugString(false); 1348 1349 if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) { 1350 Interrupt(reason); 1351 1352 // All file errors should have resulted in in file deletion above. On 1353 // resumption we will need to re-do filename determination. 1354 DCHECK(current_path_.empty()); 1355 return; 1356 } 1357 1358 DCHECK(target_path_ == full_path); 1359 1360 if (full_path != current_path_) { 1361 // full_path is now the current and target file path. 1362 DCHECK(!full_path.empty()); 1363 SetFullPath(full_path); 1364 } 1365 1366 // Complete the download and release the DownloadFile. 1367 DCHECK(download_file_.get()); 1368 ReleaseDownloadFile(false); 1369 1370 // We're not completely done with the download item yet, but at this 1371 // point we're committed to complete the download. Cancels (or Interrupts, 1372 // though it's not clear how they could happen) after this point will be 1373 // ignored. 1374 TransitionTo(COMPLETING_INTERNAL, DONT_UPDATE_OBSERVERS); 1375 1376 if (delegate_->ShouldOpenDownload( 1377 this, base::Bind(&DownloadItemImpl::DelayedDownloadOpened, 1378 weak_ptr_factory_.GetWeakPtr()))) { 1379 Completed(); 1380 } else { 1381 delegate_delayed_complete_ = true; 1382 UpdateObservers(); 1383 } 1384 } 1385 1386 void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened) { 1387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1388 1389 auto_opened_ = auto_opened; 1390 Completed(); 1391 } 1392 1393 void DownloadItemImpl::Completed() { 1394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1395 1396 VLOG(20) << __FUNCTION__ << "() " << DebugString(false); 1397 1398 DCHECK(all_data_saved_); 1399 end_time_ = base::Time::Now(); 1400 TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS); 1401 RecordDownloadCompleted(start_tick_, received_bytes_); 1402 1403 if (auto_opened_) { 1404 // If it was already handled by the delegate, do nothing. 1405 } else if (GetOpenWhenComplete() || 1406 ShouldOpenFileBasedOnExtension() || 1407 IsTemporary()) { 1408 // If the download is temporary, like in drag-and-drop, do not open it but 1409 // we still need to set it auto-opened so that it can be removed from the 1410 // download shelf. 1411 if (!IsTemporary()) 1412 OpenDownload(); 1413 1414 auto_opened_ = true; 1415 UpdateObservers(); 1416 } 1417 } 1418 1419 void DownloadItemImpl::OnResumeRequestStarted( 1420 DownloadItem* item, 1421 DownloadInterruptReason interrupt_reason) { 1422 // If |item| is not NULL, then Start() has been called already, and nothing 1423 // more needs to be done here. 1424 if (item) { 1425 DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); 1426 DCHECK_EQ(static_cast<DownloadItem*>(this), item); 1427 return; 1428 } 1429 // Otherwise, the request failed without passing through 1430 // DownloadResourceHandler::OnResponseStarted. 1431 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); 1432 Interrupt(interrupt_reason); 1433 } 1434 1435 // **** End of Download progression cascade 1436 1437 // An error occurred somewhere. 1438 void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) { 1439 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1440 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, reason); 1441 1442 // Somewhat counter-intuitively, it is possible for us to receive an 1443 // interrupt after we've already been interrupted. The generation of 1444 // interrupts from the file thread Renames and the generation of 1445 // interrupts from disk writes go through two different mechanisms (driven 1446 // by rename requests from UI thread and by write requests from IO thread, 1447 // respectively), and since we choose not to keep state on the File thread, 1448 // this is the place where the races collide. It's also possible for 1449 // interrupts to race with cancels. 1450 1451 // Whatever happens, the first one to hit the UI thread wins. 1452 if (state_ != IN_PROGRESS_INTERNAL && state_ != RESUMING_INTERNAL) 1453 return; 1454 1455 last_reason_ = reason; 1456 1457 ResumeMode resume_mode = GetResumeMode(); 1458 1459 if (state_ == IN_PROGRESS_INTERNAL) { 1460 // Cancel (delete file) if: 1461 // 1) we're going to restart. 1462 // 2) Resumption isn't possible (download was cancelled or blocked due to 1463 // security restrictions). 1464 // 3) Resumption isn't enabled. 1465 // No point in leaving data around we aren't going to use. 1466 ReleaseDownloadFile(resume_mode == RESUME_MODE_IMMEDIATE_RESTART || 1467 resume_mode == RESUME_MODE_USER_RESTART || 1468 resume_mode == RESUME_MODE_INVALID || 1469 !IsDownloadResumptionEnabled()); 1470 1471 // Cancel the originating URL request. 1472 request_handle_->CancelRequest(); 1473 } else { 1474 DCHECK(!download_file_.get()); 1475 } 1476 1477 // Reset all data saved, as even if we did save all the data we're going 1478 // to go through another round of downloading when we resume. 1479 // There's a potential problem here in the abstract, as if we did download 1480 // all the data and then run into a continuable error, on resumption we 1481 // won't download any more data. However, a) there are currently no 1482 // continuable errors that can occur after we download all the data, and 1483 // b) if there were, that would probably simply result in a null range 1484 // request, which would generate a DestinationCompleted() notification 1485 // from the DownloadFile, which would behave properly with setting 1486 // all_data_saved_ to false here. 1487 all_data_saved_ = false; 1488 1489 TransitionTo(INTERRUPTED_INTERNAL, DONT_UPDATE_OBSERVERS); 1490 RecordDownloadInterrupted(reason, received_bytes_, total_bytes_); 1491 if (!GetWebContents()) 1492 RecordDownloadCount(INTERRUPTED_WITHOUT_WEBCONTENTS); 1493 1494 AutoResumeIfValid(); 1495 UpdateObservers(); 1496 } 1497 1498 void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file) { 1499 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1500 1501 if (destroy_file) { 1502 BrowserThread::PostTask( 1503 BrowserThread::FILE, FROM_HERE, 1504 // Will be deleted at end of task execution. 1505 base::Bind(&DownloadFileCancel, base::Passed(&download_file_))); 1506 // Avoid attempting to reuse the intermediate file by clearing out 1507 // current_path_. 1508 current_path_.clear(); 1509 } else { 1510 BrowserThread::PostTask( 1511 BrowserThread::FILE, 1512 FROM_HERE, 1513 base::Bind(base::IgnoreResult(&DownloadFileDetach), 1514 // Will be deleted at end of task execution. 1515 base::Passed(&download_file_))); 1516 } 1517 // Don't accept any more messages from the DownloadFile, and null 1518 // out any previous "all data received". This also breaks links to 1519 // other entities we've given out weak pointers to. 1520 weak_ptr_factory_.InvalidateWeakPtrs(); 1521 } 1522 1523 bool DownloadItemImpl::IsDownloadReadyForCompletion( 1524 const base::Closure& state_change_notification) { 1525 // If we don't have all the data, the download is not ready for 1526 // completion. 1527 if (!AllDataSaved()) 1528 return false; 1529 1530 // If the download is dangerous, but not yet validated, it's not ready for 1531 // completion. 1532 if (IsDangerous()) 1533 return false; 1534 1535 // If the download isn't active (e.g. has been cancelled) it's not 1536 // ready for completion. 1537 if (state_ != IN_PROGRESS_INTERNAL) 1538 return false; 1539 1540 // If the target filename hasn't been determined, then it's not ready for 1541 // completion. This is checked in ReadyForDownloadCompletionDone(). 1542 if (GetTargetFilePath().empty()) 1543 return false; 1544 1545 // This is checked in NeedsRename(). Without this conditional, 1546 // browser_tests:DownloadTest.DownloadMimeType fails the DCHECK. 1547 if (target_path_.DirName() != current_path_.DirName()) 1548 return false; 1549 1550 // Give the delegate a chance to hold up a stop sign. It'll call 1551 // use back through the passed callback if it does and that state changes. 1552 if (!delegate_->ShouldCompleteDownload(this, state_change_notification)) 1553 return false; 1554 1555 return true; 1556 } 1557 1558 void DownloadItemImpl::TransitionTo(DownloadInternalState new_state, 1559 ShouldUpdateObservers notify_action) { 1560 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1561 1562 if (state_ == new_state) 1563 return; 1564 1565 DownloadInternalState old_state = state_; 1566 state_ = new_state; 1567 1568 switch (state_) { 1569 case COMPLETING_INTERNAL: 1570 bound_net_log_.AddEvent( 1571 net::NetLog::TYPE_DOWNLOAD_ITEM_COMPLETING, 1572 base::Bind(&ItemCompletingNetLogCallback, received_bytes_, &hash_)); 1573 break; 1574 case COMPLETE_INTERNAL: 1575 bound_net_log_.AddEvent( 1576 net::NetLog::TYPE_DOWNLOAD_ITEM_FINISHED, 1577 base::Bind(&ItemFinishedNetLogCallback, auto_opened_)); 1578 break; 1579 case INTERRUPTED_INTERNAL: 1580 bound_net_log_.AddEvent( 1581 net::NetLog::TYPE_DOWNLOAD_ITEM_INTERRUPTED, 1582 base::Bind(&ItemInterruptedNetLogCallback, last_reason_, 1583 received_bytes_, &hash_state_)); 1584 break; 1585 case IN_PROGRESS_INTERNAL: 1586 if (old_state == INTERRUPTED_INTERNAL) { 1587 bound_net_log_.AddEvent( 1588 net::NetLog::TYPE_DOWNLOAD_ITEM_RESUMED, 1589 base::Bind(&ItemResumingNetLogCallback, 1590 false, last_reason_, received_bytes_, &hash_state_)); 1591 } 1592 break; 1593 case CANCELLED_INTERNAL: 1594 bound_net_log_.AddEvent( 1595 net::NetLog::TYPE_DOWNLOAD_ITEM_CANCELED, 1596 base::Bind(&ItemCanceledNetLogCallback, received_bytes_, 1597 &hash_state_)); 1598 break; 1599 default: 1600 break; 1601 } 1602 1603 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true) 1604 << " " << InternalToExternalState(old_state) 1605 << " " << InternalToExternalState(state_); 1606 1607 bool is_done = (state_ != IN_PROGRESS_INTERNAL && 1608 state_ != COMPLETING_INTERNAL); 1609 bool was_done = (old_state != IN_PROGRESS_INTERNAL && 1610 old_state != COMPLETING_INTERNAL); 1611 // Termination 1612 if (is_done && !was_done) 1613 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE); 1614 1615 // Resumption 1616 if (was_done && !is_done) { 1617 std::string file_name(target_path_.BaseName().AsUTF8Unsafe()); 1618 bound_net_log_.BeginEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, 1619 base::Bind(&ItemActivatedNetLogCallback, 1620 this, SRC_ACTIVE_DOWNLOAD, 1621 &file_name)); 1622 } 1623 1624 if (notify_action == UPDATE_OBSERVERS) 1625 UpdateObservers(); 1626 } 1627 1628 void DownloadItemImpl::SetDangerType(DownloadDangerType danger_type) { 1629 if (danger_type != danger_type_) { 1630 bound_net_log_.AddEvent( 1631 net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED, 1632 base::Bind(&ItemCheckedNetLogCallback, danger_type)); 1633 } 1634 // Only record the Malicious UMA stat if it's going from {not malicious} -> 1635 // {malicious}. 1636 if ((danger_type_ == DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS || 1637 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE || 1638 danger_type_ == DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT || 1639 danger_type_ == DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT) && 1640 (danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST || 1641 danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL || 1642 danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT || 1643 danger_type == DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED)) { 1644 RecordMaliciousDownloadClassified(danger_type); 1645 } 1646 danger_type_ = danger_type; 1647 } 1648 1649 void DownloadItemImpl::SetFullPath(const base::FilePath& new_path) { 1650 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1651 VLOG(20) << __FUNCTION__ << "()" 1652 << " new_path = \"" << new_path.value() << "\"" 1653 << " " << DebugString(true); 1654 DCHECK(!new_path.empty()); 1655 1656 bound_net_log_.AddEvent( 1657 net::NetLog::TYPE_DOWNLOAD_ITEM_RENAMED, 1658 base::Bind(&ItemRenamedNetLogCallback, ¤t_path_, &new_path)); 1659 1660 current_path_ = new_path; 1661 } 1662 1663 void DownloadItemImpl::AutoResumeIfValid() { 1664 DVLOG(20) << __FUNCTION__ << "() " << DebugString(true); 1665 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1666 ResumeMode mode = GetResumeMode(); 1667 1668 if (mode != RESUME_MODE_IMMEDIATE_RESTART && 1669 mode != RESUME_MODE_IMMEDIATE_CONTINUE) { 1670 return; 1671 } 1672 1673 auto_resume_count_++; 1674 1675 ResumeInterruptedDownload(); 1676 } 1677 1678 void DownloadItemImpl::ResumeInterruptedDownload() { 1679 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1680 1681 // If the flag for downloads resumption isn't enabled, ignore 1682 // this request. 1683 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 1684 if (!command_line.HasSwitch(switches::kEnableDownloadResumption)) 1685 return; 1686 1687 // If we're not interrupted, ignore the request; our caller is drunk. 1688 if (state_ != INTERRUPTED_INTERNAL) 1689 return; 1690 1691 // If we can't get a web contents, we can't resume the download. 1692 // TODO(rdsmith): Find some alternative web contents to use--this 1693 // means we can't restart a download if it's a download imported 1694 // from the history. 1695 if (!GetWebContents()) 1696 return; 1697 1698 // Reset the appropriate state if restarting. 1699 ResumeMode mode = GetResumeMode(); 1700 if (mode == RESUME_MODE_IMMEDIATE_RESTART || 1701 mode == RESUME_MODE_USER_RESTART) { 1702 received_bytes_ = 0; 1703 hash_state_ = ""; 1704 last_modified_time_ = ""; 1705 etag_ = ""; 1706 } 1707 1708 scoped_ptr<DownloadUrlParameters> download_params( 1709 DownloadUrlParameters::FromWebContents(GetWebContents(), 1710 GetOriginalUrl())); 1711 1712 download_params->set_file_path(GetFullPath()); 1713 download_params->set_offset(GetReceivedBytes()); 1714 download_params->set_hash_state(GetHashState()); 1715 download_params->set_last_modified(GetLastModifiedTime()); 1716 download_params->set_etag(GetETag()); 1717 download_params->set_callback( 1718 base::Bind(&DownloadItemImpl::OnResumeRequestStarted, 1719 weak_ptr_factory_.GetWeakPtr())); 1720 1721 delegate_->ResumeInterruptedDownload(download_params.Pass(), GetId()); 1722 // Just in case we were interrupted while paused. 1723 is_paused_ = false; 1724 1725 TransitionTo(RESUMING_INTERNAL, DONT_UPDATE_OBSERVERS); 1726 } 1727 1728 // static 1729 DownloadItem::DownloadState DownloadItemImpl::InternalToExternalState( 1730 DownloadInternalState internal_state) { 1731 switch (internal_state) { 1732 case IN_PROGRESS_INTERNAL: 1733 return IN_PROGRESS; 1734 case COMPLETING_INTERNAL: 1735 return IN_PROGRESS; 1736 case COMPLETE_INTERNAL: 1737 return COMPLETE; 1738 case CANCELLED_INTERNAL: 1739 return CANCELLED; 1740 case INTERRUPTED_INTERNAL: 1741 return INTERRUPTED; 1742 case RESUMING_INTERNAL: 1743 return INTERRUPTED; 1744 case MAX_DOWNLOAD_INTERNAL_STATE: 1745 break; 1746 } 1747 NOTREACHED(); 1748 return MAX_DOWNLOAD_STATE; 1749 } 1750 1751 // static 1752 DownloadItemImpl::DownloadInternalState 1753 DownloadItemImpl::ExternalToInternalState( 1754 DownloadState external_state) { 1755 switch (external_state) { 1756 case IN_PROGRESS: 1757 return IN_PROGRESS_INTERNAL; 1758 case COMPLETE: 1759 return COMPLETE_INTERNAL; 1760 case CANCELLED: 1761 return CANCELLED_INTERNAL; 1762 case INTERRUPTED: 1763 return INTERRUPTED_INTERNAL; 1764 default: 1765 NOTREACHED(); 1766 } 1767 return MAX_DOWNLOAD_INTERNAL_STATE; 1768 } 1769 1770 const char* DownloadItemImpl::DebugDownloadStateString( 1771 DownloadInternalState state) { 1772 switch (state) { 1773 case IN_PROGRESS_INTERNAL: 1774 return "IN_PROGRESS"; 1775 case COMPLETING_INTERNAL: 1776 return "COMPLETING"; 1777 case COMPLETE_INTERNAL: 1778 return "COMPLETE"; 1779 case CANCELLED_INTERNAL: 1780 return "CANCELLED"; 1781 case INTERRUPTED_INTERNAL: 1782 return "INTERRUPTED"; 1783 case RESUMING_INTERNAL: 1784 return "RESUMING"; 1785 case MAX_DOWNLOAD_INTERNAL_STATE: 1786 break; 1787 }; 1788 NOTREACHED() << "Unknown download state " << state; 1789 return "unknown"; 1790 } 1791 1792 const char* DownloadItemImpl::DebugResumeModeString(ResumeMode mode) { 1793 switch (mode) { 1794 case RESUME_MODE_INVALID: 1795 return "INVALID"; 1796 case RESUME_MODE_IMMEDIATE_CONTINUE: 1797 return "IMMEDIATE_CONTINUE"; 1798 case RESUME_MODE_IMMEDIATE_RESTART: 1799 return "IMMEDIATE_RESTART"; 1800 case RESUME_MODE_USER_CONTINUE: 1801 return "USER_CONTINUE"; 1802 case RESUME_MODE_USER_RESTART: 1803 return "USER_RESTART"; 1804 } 1805 NOTREACHED() << "Unknown resume mode " << mode; 1806 return "unknown"; 1807 } 1808 1809 } // namespace content 1810