OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/download/download_manager.h" | 5 #include "content/browser/download/download_manager.h" |
6 | 6 |
7 #include <iterator> | 7 #include <iterator> |
8 | 8 |
9 #include "base/callback.h" | 9 #include "base/callback.h" |
10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
105 // The user hasn't accepted it, so we need to remove it | 105 // The user hasn't accepted it, so we need to remove it |
106 // from the disk. This may or may not result in it being | 106 // from the disk. This may or may not result in it being |
107 // removed from the DownloadManager queues and deleted | 107 // removed from the DownloadManager queues and deleted |
108 // (specifically, DownloadManager::RemoveDownload only | 108 // (specifically, DownloadManager::RemoveDownload only |
109 // removes and deletes it if it's known to the history service) | 109 // removes and deletes it if it's known to the history service) |
110 // so the only thing we know after calling this function is that | 110 // so the only thing we know after calling this function is that |
111 // the download was deleted if-and-only-if it was removed | 111 // the download was deleted if-and-only-if it was removed |
112 // from all queues. | 112 // from all queues. |
113 download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN); | 113 download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN); |
114 } else if (download->IsPartialDownload()) { | 114 } else if (download->IsPartialDownload()) { |
115 download->Cancel(false); | 115 download->Cancel(); |
116 delegate_->UpdateItemInPersistentStore(download); | |
117 } | 116 } |
118 } | 117 } |
119 | 118 |
120 // At this point, all dangerous downloads have had their files removed | 119 // At this point, all dangerous downloads have had their files removed |
121 // and all in progress downloads have been cancelled. We can now delete | 120 // and all in progress downloads have been cancelled. We can now delete |
122 // anything left. | 121 // anything left. |
123 | 122 |
124 // Copy downloads_ to separate container so as not to set off checks | 123 // Copy downloads_ to separate container so as not to set off checks |
125 // in DownloadItem destruction. | 124 // in DownloadItem destruction. |
126 DownloadSet downloads_to_delete; | 125 DownloadSet downloads_to_delete; |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 << " download = " << download->DebugString(true); | 267 << " download = " << download->DebugString(true); |
269 | 268 |
270 FilePath suggested_path = download->suggested_path(); | 269 FilePath suggested_path = download->suggested_path(); |
271 | 270 |
272 if (download->prompt_user_for_save_location()) { | 271 if (download->prompt_user_for_save_location()) { |
273 // We must ask the user for the place to put the download. | 272 // We must ask the user for the place to put the download. |
274 DownloadRequestHandle request_handle = download->request_handle(); | 273 DownloadRequestHandle request_handle = download->request_handle(); |
275 TabContents* contents = request_handle.GetTabContents(); | 274 TabContents* contents = request_handle.GetTabContents(); |
276 | 275 |
277 // |id_ptr| will be deleted in either FileSelected() or | 276 // |id_ptr| will be deleted in either FileSelected() or |
278 // FileSelectionCancelled(). | 277 // FileSelectionCanceled(). |
279 int32* id_ptr = new int32; | 278 int32* id_ptr = new int32; |
280 *id_ptr = download_id; | 279 *id_ptr = download_id; |
281 | 280 |
282 delegate_->ChooseDownloadPath( | 281 delegate_->ChooseDownloadPath( |
283 contents, suggested_path, reinterpret_cast<void*>(id_ptr)); | 282 contents, suggested_path, reinterpret_cast<void*>(id_ptr)); |
284 | 283 |
285 FOR_EACH_OBSERVER(Observer, observers_, | 284 FOR_EACH_OBSERVER(Observer, observers_, |
286 SelectFileDialogDisplayed(download_id)); | 285 SelectFileDialogDisplayed(download_id)); |
287 } else { | 286 } else { |
288 // No prompting for download, just continue with the suggested name. | 287 // No prompting for download, just continue with the suggested name. |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 NewRunnableMethod( | 497 NewRunnableMethod( |
499 file_manager_, &DownloadFileManager::CompleteDownload, download_id)); | 498 file_manager_, &DownloadFileManager::CompleteDownload, download_id)); |
500 | 499 |
501 if (uniquifier) | 500 if (uniquifier) |
502 item->set_path_uniquifier(uniquifier); | 501 item->set_path_uniquifier(uniquifier); |
503 | 502 |
504 item->OnDownloadRenamedToFinalName(full_path); | 503 item->OnDownloadRenamedToFinalName(full_path); |
505 delegate_->UpdatePathForItemInPersistentStore(item, full_path); | 504 delegate_->UpdatePathForItemInPersistentStore(item, full_path); |
506 } | 505 } |
507 | 506 |
508 void DownloadManager::CancelDownload(int32 download_id) { | 507 void DownloadManager::DownloadStopped(DownloadItem* download) { |
509 DownloadItem* download = GetActiveDownload(download_id); | |
510 // A cancel at the right time could remove the download from the | |
511 // |active_downloads_| map before we get here. | |
512 if (!download) | |
513 return; | |
514 | |
515 download->Cancel(true); | |
516 } | |
517 | |
518 void DownloadManager::DownloadCancelledInternal(DownloadItem* download) { | |
519 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 509 CHECK(ContainsKey(active_downloads_, download->id())); |
520 | 510 |
521 VLOG(20) << __FUNCTION__ << "()" | 511 VLOG(20) << __FUNCTION__ << "()" |
522 << " download = " << download->DebugString(true); | 512 << " download = " << download->DebugString(true); |
523 | 513 |
524 RemoveFromActiveList(download); | 514 in_progress_.erase(download->id()); |
| 515 active_downloads_.erase(download->id()); |
| 516 UpdateDownloadProgress(); // Reflect removal from in_progress_. |
| 517 if (download->db_handle() != DownloadItem::kUninitializedHandle) |
| 518 delegate_->UpdateItemInPersistentStore(download); |
| 519 |
525 // This function is called from the DownloadItem, so DI state | 520 // This function is called from the DownloadItem, so DI state |
526 // should already have been updated. | 521 // should already have been updated. |
527 AssertQueueStateConsistent(download); | 522 AssertQueueStateConsistent(download); |
528 | 523 |
529 download->OffThreadCancel(file_manager_); | 524 download->OffThreadCancel(file_manager_); |
530 } | 525 } |
531 | 526 |
532 void DownloadManager::OnDownloadError(int32 download_id, | 527 void DownloadManager::OnDownloadError(int32 download_id, |
533 int64 size, | 528 int64 size, |
534 net::Error error) { | 529 net::Error error) { |
535 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 530 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
536 | 531 |
537 DownloadItem* download = GetActiveDownload(download_id); | 532 DownloadItem* download = GetActiveDownload(download_id); |
538 if (!download) | 533 if (!download) |
539 return; | 534 return; |
540 | 535 |
541 VLOG(20) << __FUNCTION__ << "()" << " Error " << error | 536 VLOG(20) << __FUNCTION__ << "()" << " Error " << error |
542 << " at offset " << download->received_bytes() | 537 << " at offset " << download->received_bytes() |
543 << " size = " << size | 538 << " size = " << size |
544 << " download = " << download->DebugString(true); | 539 << " download = " << download->DebugString(true); |
545 | 540 |
546 RemoveFromActiveList(download); | 541 download->Interrupt(size, error); |
547 download->Interrupted(size, error); | |
548 download->OffThreadCancel(file_manager_); | |
549 } | 542 } |
550 | 543 |
551 DownloadItem* DownloadManager::GetActiveDownload(int32 download_id) { | 544 DownloadItem* DownloadManager::GetActiveDownload(int32 download_id) { |
552 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 545 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
553 DownloadMap::iterator it = active_downloads_.find(download_id); | 546 DownloadMap::iterator it = active_downloads_.find(download_id); |
554 if (it == active_downloads_.end()) | 547 if (it == active_downloads_.end()) |
555 return NULL; | 548 return NULL; |
556 | 549 |
557 DownloadItem* download = it->second; | 550 DownloadItem* download = it->second; |
558 | 551 |
559 DCHECK(download); | 552 DCHECK(download); |
560 DCHECK_EQ(download_id, download->id()); | 553 DCHECK_EQ(download_id, download->id()); |
561 | 554 |
562 return download; | 555 return download; |
563 } | 556 } |
564 | 557 |
565 void DownloadManager::RemoveFromActiveList(DownloadItem* download) { | |
566 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
567 DCHECK(download); | |
568 | |
569 // Clean up will happen when the history system create callback runs if we | |
570 // don't have a valid db_handle yet. | |
571 if (download->db_handle() != DownloadItem::kUninitializedHandle) { | |
572 in_progress_.erase(download->id()); | |
573 active_downloads_.erase(download->id()); | |
574 UpdateDownloadProgress(); // Reflect removal from in_progress_. | |
575 delegate_->UpdateItemInPersistentStore(download); | |
576 } | |
577 } | |
578 | |
579 void DownloadManager::UpdateDownloadProgress() { | 558 void DownloadManager::UpdateDownloadProgress() { |
580 delegate_->DownloadProgressUpdated(); | 559 delegate_->DownloadProgressUpdated(); |
581 } | 560 } |
582 | 561 |
583 int DownloadManager::RemoveDownloadItems( | 562 int DownloadManager::RemoveDownloadItems( |
584 const DownloadVector& pending_deletes) { | 563 const DownloadVector& pending_deletes) { |
585 if (pending_deletes.empty()) | 564 if (pending_deletes.empty()) |
586 return 0; | 565 return 0; |
587 | 566 |
588 // Delete from internal maps. | 567 // Delete from internal maps. |
589 for (DownloadVector::const_iterator it = pending_deletes.begin(); | 568 for (DownloadVector::const_iterator it = pending_deletes.begin(); |
590 it != pending_deletes.end(); | 569 it != pending_deletes.end(); |
591 ++it) { | 570 ++it) { |
592 DownloadItem* download = *it; | 571 DownloadItem* download = *it; |
593 DCHECK(download); | 572 DCHECK(download); |
594 history_downloads_.erase(download->db_handle()); | 573 history_downloads_.erase(download->db_handle()); |
595 save_page_downloads_.erase(download->id()); | 574 save_page_downloads_.erase(download->id()); |
596 downloads_.erase(download); | 575 downloads_.erase(download); |
597 } | 576 } |
598 | 577 |
599 // Tell observers to refresh their views. | 578 // Tell observers to refresh their views. |
600 NotifyModelChanged(); | 579 NotifyModelChanged(); |
601 | 580 |
602 // Delete the download items themselves. | 581 // Delete the download items themselves. |
603 const int num_deleted = static_cast<int>(pending_deletes.size()); | 582 const int num_deleted = static_cast<int>(pending_deletes.size()); |
604 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end()); | 583 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end()); |
605 return num_deleted; | 584 return num_deleted; |
606 } | 585 } |
607 | 586 |
608 void DownloadManager::RemoveDownload(int64 download_handle) { | 587 void DownloadManager::RemoveDownload(DownloadItem* download) { |
609 DownloadMap::iterator it = history_downloads_.find(download_handle); | 588 // Make history update. Ignores if db_handle isn't in history. |
610 if (it == history_downloads_.end()) | 589 delegate_->RemoveItemFromPersistentStore(download->db_handle()); |
611 return; | |
612 | |
613 // Make history update. | |
614 DownloadItem* download = it->second; | |
615 delegate_->RemoveItemFromPersistentStore(download); | |
616 | 590 |
617 // Remove from our tables and delete. | 591 // Remove from our tables and delete. |
618 int downloads_count = RemoveDownloadItems(DownloadVector(1, download)); | 592 int downloads_count = RemoveDownloadItems(DownloadVector(1, download)); |
619 DCHECK_EQ(1, downloads_count); | 593 DCHECK_EQ(1, downloads_count); |
620 } | 594 } |
621 | 595 |
622 int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin, | 596 int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin, |
623 const base::Time remove_end) { | 597 const base::Time remove_end) { |
624 delegate_->RemoveItemsFromPersistentStoreBetween(remove_begin, remove_end); | 598 delegate_->RemoveItemsFromPersistentStoreBetween(remove_begin, remove_end); |
625 | 599 |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
755 int32 download_id = *id_ptr; | 729 int32 download_id = *id_ptr; |
756 delete id_ptr; | 730 delete id_ptr; |
757 | 731 |
758 DownloadItem* download = GetActiveDownloadItem(download_id); | 732 DownloadItem* download = GetActiveDownloadItem(download_id); |
759 if (!download) | 733 if (!download) |
760 return; | 734 return; |
761 | 735 |
762 VLOG(20) << __FUNCTION__ << "()" | 736 VLOG(20) << __FUNCTION__ << "()" |
763 << " download = " << download->DebugString(true); | 737 << " download = " << download->DebugString(true); |
764 | 738 |
765 // TODO(ahendrickson) -- This currently has no effect, as the download is | 739 download->Cancel(); |
766 // not put on the active list until the file selection is complete. Need | |
767 // to put it on the active list earlier in the process. | |
768 RemoveFromActiveList(download); | |
769 | |
770 download->OffThreadCancel(file_manager_); | |
771 } | 740 } |
772 | 741 |
773 // Operations posted to us from the history service ---------------------------- | 742 // Operations posted to us from the history service ---------------------------- |
774 | 743 |
775 // The history service has retrieved all download entries. 'entries' contains | 744 // The history service has retrieved all download entries. 'entries' contains |
776 // 'DownloadPersistentStoreInfo's in sorted order (by ascending start_time). | 745 // 'DownloadPersistentStoreInfo's in sorted order (by ascending start_time). |
777 void DownloadManager::OnPersistentStoreQueryComplete( | 746 void DownloadManager::OnPersistentStoreQueryComplete( |
778 std::vector<DownloadPersistentStoreInfo>* entries) { | 747 std::vector<DownloadPersistentStoreInfo>* entries) { |
779 // TODO(rdsmith): Remove this and related logic when | 748 // TODO(rdsmith): Remove this and related logic when |
780 // http://crbug.com/84508 is fixed. | 749 // http://crbug.com/84508 is fixed. |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
831 // It's valid that we don't find a matching item, i.e. on shutdown. | 800 // It's valid that we don't find a matching item, i.e. on shutdown. |
832 } | 801 } |
833 | 802 |
834 // Once the new DownloadItem's creation info has been committed to the history | 803 // Once the new DownloadItem's creation info has been committed to the history |
835 // service, we associate the DownloadItem with the db handle, update our | 804 // service, we associate the DownloadItem with the db handle, update our |
836 // 'history_downloads_' map and inform observers. | 805 // 'history_downloads_' map and inform observers. |
837 void DownloadManager::OnDownloadItemAddedToPersistentStore(int32 download_id, | 806 void DownloadManager::OnDownloadItemAddedToPersistentStore(int32 download_id, |
838 int64 db_handle) { | 807 int64 db_handle) { |
839 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 808 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
840 DownloadItem* download = GetActiveDownloadItem(download_id); | 809 DownloadItem* download = GetActiveDownloadItem(download_id); |
841 if (!download) | 810 if (!download) { |
| 811 // The download was cancelled while the persistent store was entering it. |
| 812 // We resolve this race by turning around and deleting it in the |
| 813 // persistent store (implicitly treating it as a failure in download |
| 814 // initiation, which is appropriate as the only places the cancel could |
| 815 // have come from were in resolving issues (like the file name) which |
| 816 // we need to have resolved for persistent store insertion). |
| 817 |
| 818 // Make sure we haven't already been shutdown (the callback raced |
| 819 // with shutdown), as that would mean that we no longer have access |
| 820 // to the persistent store. In that case, the history will be cleaned up |
| 821 // on next persistent store query. |
| 822 if (shutdown_needed_) |
| 823 delegate_->RemoveItemFromPersistentStore(db_handle); |
842 return; | 824 return; |
| 825 } |
843 | 826 |
844 VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle | 827 VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle |
845 << " download_id = " << download_id | 828 << " download_id = " << download_id |
846 << " download = " << download->DebugString(true); | 829 << " download = " << download->DebugString(true); |
847 | 830 |
848 // TODO(rdsmith): Remove after http://crbug.com/85408 resolved. | 831 // TODO(rdsmith): Remove after http://crbug.com/85408 resolved. |
849 int64 largest_handle = largest_db_handle_in_history_; | 832 int64 largest_handle = largest_db_handle_in_history_; |
850 base::debug::Alias(&largest_handle); | 833 base::debug::Alias(&largest_handle); |
851 int32 matching_item_download_id | 834 int32 matching_item_download_id |
852 = (ContainsKey(history_downloads_, db_handle) ? | 835 = (ContainsKey(history_downloads_, db_handle) ? |
853 history_downloads_[db_handle]->id() : 0); | 836 history_downloads_[db_handle]->id() : 0); |
854 base::debug::Alias(&matching_item_download_id); | 837 base::debug::Alias(&matching_item_download_id); |
855 | 838 |
856 CHECK(!ContainsKey(history_downloads_, db_handle)); | 839 CHECK(!ContainsKey(history_downloads_, db_handle)); |
857 | 840 |
| 841 CHECK(download->IsInProgress()); |
858 AddDownloadItemToHistory(download, db_handle); | 842 AddDownloadItemToHistory(download, db_handle); |
859 | 843 |
860 // If the download is still in progress, try to complete it. | 844 MaybeCompleteDownload(download); |
861 // | |
862 // Otherwise, download has been cancelled or interrupted before we've | |
863 // received the DB handle. We post one final message to the history | |
864 // service so that it can be properly in sync with the DownloadItem's | |
865 // completion status, and also inform any observers so that they get | |
866 // more than just the start notification. | |
867 if (download->IsInProgress()) { | |
868 MaybeCompleteDownload(download); | |
869 } else { | |
870 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/84508 | |
871 // is fixed. | |
872 CHECK(download->IsCancelled()) | |
873 << " download = " << download->DebugString(true); | |
874 in_progress_.erase(download_id); | |
875 active_downloads_.erase(download_id); | |
876 delegate_->UpdateItemInPersistentStore(download); | |
877 download->UpdateObservers(); | |
878 } | |
879 } | 845 } |
880 | 846 |
881 void DownloadManager::ShowDownloadInBrowser(DownloadItem* download) { | 847 void DownloadManager::ShowDownloadInBrowser(DownloadItem* download) { |
882 // The 'contents' may no longer exist if the user closed the tab before we | 848 // The 'contents' may no longer exist if the user closed the tab before we |
883 // get this start completion event. | 849 // get this start completion event. |
884 DownloadRequestHandle request_handle = download->request_handle(); | 850 DownloadRequestHandle request_handle = download->request_handle(); |
885 TabContents* content = request_handle.GetTabContents(); | 851 TabContents* content = request_handle.GetTabContents(); |
886 | 852 |
887 // If the contents no longer exists, we ask the embedder to suggest another | 853 // If the contents no longer exists, we ask the embedder to suggest another |
888 // tab. | 854 // tab. |
(...skipping 18 matching lines...) Expand all Loading... |
907 // not its id, so we have to iterate. | 873 // not its id, so we have to iterate. |
908 for (DownloadMap::iterator it = history_downloads_.begin(); | 874 for (DownloadMap::iterator it = history_downloads_.begin(); |
909 it != history_downloads_.end(); ++it) { | 875 it != history_downloads_.end(); ++it) { |
910 DownloadItem* item = it->second; | 876 DownloadItem* item = it->second; |
911 if (item->id() == download_id) | 877 if (item->id() == download_id) |
912 return item; | 878 return item; |
913 } | 879 } |
914 return NULL; | 880 return NULL; |
915 } | 881 } |
916 | 882 |
| 883 void DownloadManager::GetInProgressDownloads( |
| 884 std::vector<DownloadItem*>* result) { |
| 885 DCHECK(result); |
| 886 |
| 887 for (DownloadMap::iterator it = active_downloads_.begin(); |
| 888 it != active_downloads_.end(); ++it) { |
| 889 result->push_back(it->second); |
| 890 } |
| 891 } |
| 892 |
917 DownloadItem* DownloadManager::GetActiveDownloadItem(int download_id) { | 893 DownloadItem* DownloadManager::GetActiveDownloadItem(int download_id) { |
918 DCHECK(ContainsKey(active_downloads_, download_id)); | 894 if (!ContainsKey(active_downloads_, download_id)) |
| 895 return NULL; |
| 896 |
919 DownloadItem* download = active_downloads_[download_id]; | 897 DownloadItem* download = active_downloads_[download_id]; |
920 DCHECK(download != NULL); | 898 DCHECK(download != NULL); |
921 return download; | 899 return download; |
922 } | 900 } |
923 | 901 |
924 // Confirm that everything in all maps is also in |downloads_|, and that | 902 // Confirm that everything in all maps is also in |downloads_|, and that |
925 // everything in |downloads_| is also in some other map. | 903 // everything in |downloads_| is also in some other map. |
926 void DownloadManager::AssertContainersConsistent() const { | 904 void DownloadManager::AssertContainersConsistent() const { |
927 #if !defined(NDEBUG) | 905 #if !defined(NDEBUG) |
928 // Turn everything into sets. | 906 // Turn everything into sets. |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1024 DCHECK(ContainsKey(save_page_downloads_, download->id())); | 1002 DCHECK(ContainsKey(save_page_downloads_, download->id())); |
1025 save_page_downloads_.erase(download->id()); | 1003 save_page_downloads_.erase(download->id()); |
1026 | 1004 |
1027 if (download->IsComplete()) | 1005 if (download->IsComplete()) |
1028 NotificationService::current()->Notify( | 1006 NotificationService::current()->Notify( |
1029 content::NOTIFICATION_SAVE_PACKAGE_SUCCESSFULLY_FINISHED, | 1007 content::NOTIFICATION_SAVE_PACKAGE_SUCCESSFULLY_FINISHED, |
1030 Source<DownloadManager>(this), | 1008 Source<DownloadManager>(this), |
1031 Details<DownloadItem>(download)); | 1009 Details<DownloadItem>(download)); |
1032 } | 1010 } |
1033 } | 1011 } |
OLD | NEW |