diff options
Diffstat (limited to 'chrome/browser/download/download_manager.cc')
-rw-r--r-- | chrome/browser/download/download_manager.cc | 285 |
1 files changed, 177 insertions, 108 deletions
diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc index 746dbed..da878b0 100644 --- a/chrome/browser/download/download_manager.cc +++ b/chrome/browser/download/download_manager.cc @@ -20,25 +20,26 @@ #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" +#include "chrome/browser/download/download_extensions.h" #include "chrome/browser/download/download_file_manager.h" #include "chrome/browser/download/download_history.h" #include "chrome/browser/download/download_item.h" #include "chrome/browser/download/download_prefs.h" #include "chrome/browser/download/download_status_updater.h" #include "chrome/browser/download/download_util.h" -#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/history/download_create_info.h" #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/platform_util.h" -#include "chrome/browser/profile.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/browser/tab_contents/infobar_delegate.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_paths.h" -#include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" #include "googleurl/src/gurl.h" @@ -79,56 +80,49 @@ void DownloadManager::Shutdown() { BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, NewRunnableMethod(file_manager_, &DownloadFileManager::OnDownloadManagerShutdown, - this)); + make_scoped_refptr(this))); } - // 'in_progress_' may contain DownloadItems that have not finished the start - // complete (from the history service) and thus aren't in downloads_. - DownloadMap::iterator it = in_progress_.begin(); - std::set<DownloadItem*> to_remove; - for (; it != in_progress_.end(); ++it) { - DownloadItem* download = it->second; - if (download->safety_state() == DownloadItem::DANGEROUS) { - // Forget about any download that the user did not approve. - // Note that we cannot call download->Remove() this would invalidate our - // iterator. - to_remove.insert(download); - continue; - } - DCHECK_EQ(DownloadItem::IN_PROGRESS, download->state()); - download->Cancel(false); - download_history_->UpdateEntry(download); - if (download->db_handle() == DownloadHistory::kUninitializedHandle) { - // An invalid handle means that 'download' does not yet exist in - // 'downloads_', so we have to delete it here. - delete download; + AssertContainersConsistent(); + + // Go through all downloads in downloads_. Dangerous ones we need to + // remove on disk, and in progress ones we need to cancel. + for (DownloadSet::iterator it = downloads_.begin(); it != downloads_.end();) { + DownloadItem* download = *it; + + // Save iterator from potential erases in this set done by called code. + // Iterators after an erasure point are still valid for lists and + // associative containers such as sets. + it++; + + if (download->safety_state() == DownloadItem::DANGEROUS && + (download->state() == DownloadItem::IN_PROGRESS || + download->state() == DownloadItem::COMPLETE)) { + // The user hasn't accepted it, so we need to remove it + // from the disk. This may or may not result in it being + // removed from the DownloadManager queues and deleted + // (specifically, DownloadManager::RemoveDownload only + // removes and deletes it if it's known to the history service) + // so the only thing we know after calling this function is that + // the download was deleted if-and-only-if it was removed + // from all queues. + download->Remove(true); + } else if (download->state() == DownloadItem::IN_PROGRESS) { + download->Cancel(false); + download_history_->UpdateEntry(download); } } - // 'dangerous_finished_' contains all complete downloads that have not been - // approved. They should be removed. - it = dangerous_finished_.begin(); - for (; it != dangerous_finished_.end(); ++it) - to_remove.insert(it->second); - - // Remove the dangerous download that are not approved. - for (std::set<DownloadItem*>::const_iterator rm_it = to_remove.begin(); - rm_it != to_remove.end(); ++rm_it) { - DownloadItem* download = *rm_it; - int64 handle = download->db_handle(); - download->Remove(true); - // Same as above, delete the download if it is not in 'downloads_' (as the - // Remove() call above won't have deleted it). - if (handle == DownloadHistory::kUninitializedHandle) - delete download; - } - to_remove.clear(); + // At this point, all dangerous downloads have had their files removed + // and all in progress downloads have been cancelled. We can now delete + // anything left. + STLDeleteElements(&downloads_); + // And clear all non-owning containers. in_progress_.clear(); - dangerous_finished_.clear(); - STLDeleteValues(&downloads_); - STLDeleteContainerPointers(save_page_downloads_.begin(), - save_page_downloads_.end()); +#if !defined(NDEBUG) + save_page_as_downloads_.clear(); +#endif file_manager_ = NULL; @@ -148,8 +142,8 @@ void DownloadManager::GetTemporaryDownloads( const FilePath& dir_path, std::vector<DownloadItem*>* result) { DCHECK(result); - for (DownloadMap::iterator it = downloads_.begin(); - it != downloads_.end(); ++it) { + for (DownloadMap::iterator it = history_downloads_.begin(); + it != history_downloads_.end(); ++it) { if (it->second->is_temporary() && it->second->full_path().DirName() == dir_path) result->push_back(it->second); @@ -160,8 +154,8 @@ void DownloadManager::GetAllDownloads( const FilePath& dir_path, std::vector<DownloadItem*>* result) { DCHECK(result); - for (DownloadMap::iterator it = downloads_.begin(); - it != downloads_.end(); ++it) { + for (DownloadMap::iterator it = history_downloads_.begin(); + it != history_downloads_.end(); ++it) { if (!it->second->is_temporary() && (dir_path.empty() || it->second->full_path().DirName() == dir_path)) result->push_back(it->second); @@ -172,8 +166,8 @@ void DownloadManager::GetCurrentDownloads( const FilePath& dir_path, std::vector<DownloadItem*>* result) { DCHECK(result); - for (DownloadMap::iterator it = downloads_.begin(); - it != downloads_.end(); ++it) { + for (DownloadMap::iterator it = history_downloads_.begin(); + it != history_downloads_.end(); ++it) { if (!it->second->is_temporary() && (it->second->state() == DownloadItem::IN_PROGRESS || it->second->safety_state() == DownloadItem::DANGEROUS) && @@ -186,7 +180,6 @@ void DownloadManager::GetCurrentDownloads( if (original_profile != profile_) original_profile->GetDownloadManager()->GetCurrentDownloads(dir_path, result); - } void DownloadManager::SearchDownloads(const string16& query, @@ -195,8 +188,8 @@ void DownloadManager::SearchDownloads(const string16& query, string16 query_lower(l10n_util::ToLower(query)); - for (DownloadMap::iterator it = downloads_.begin(); - it != downloads_.end(); ++it) { + for (DownloadMap::iterator it = history_downloads_.begin(); + it != history_downloads_.end(); ++it) { DownloadItem* download_item = it->second; if (download_item->is_temporary() || download_item->is_extension_install()) @@ -251,9 +244,10 @@ bool DownloadManager::Init(Profile* profile) { // create a download item and store it in our download map, and inform the // history system of a new download. Since this method can be called while the // history service thread is still reading the persistent state, we do not -// insert the new DownloadItem into 'downloads_' or inform our observers at this -// point. OnCreateDatabaseEntryComplete() handles that finalization of the the -// download creation as a callback from the history thread. +// insert the new DownloadItem into 'history_downloads_' or inform our +// observers at this point. OnCreateDatabaseEntryComplete() handles that +// finalization of the the download creation as a callback from the +// history thread. void DownloadManager::StartDownload(DownloadCreateInfo* info) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(info); @@ -288,7 +282,7 @@ void DownloadManager::StartDownload(DownloadCreateInfo* info) { // Determine the proper path for a download, by either one of the following: // 1) using the default download directory. // 2) prompting the user. - if (info->prompt_user_for_save_location && !last_download_path_.empty()){ + if (info->prompt_user_for_save_location && !last_download_path_.empty()) { info->suggested_path = last_download_path_; } else { info->suggested_path = download_prefs_->download_path(); @@ -423,28 +417,58 @@ void DownloadManager::OnPathExistenceAvailable(DownloadCreateInfo* info) { FOR_EACH_OBSERVER(Observer, observers_, SelectFileDialogDisplayed()); } else { // No prompting for download, just continue with the suggested name. - CreateDownloadItem(info, info->suggested_path); + AttachDownloadItem(info, info->suggested_path); } } -void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info, +void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + DownloadItem* download = new DownloadItem(this, *info, + profile_->IsOffTheRecord()); + DCHECK(!ContainsKey(in_progress_, info->download_id)); + downloads_.insert(download); +} + +void DownloadManager::AttachDownloadItem(DownloadCreateInfo* info, const FilePath& target_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); scoped_ptr<DownloadCreateInfo> infop(info); info->path = target_path; - DownloadItem* download = new DownloadItem(this, *info, - profile_->IsOffTheRecord()); + // NOTE(ahendrickson) We will be adding a new map |active_downloads_|, into + // which we will be adding the download as soon as it's created. This will + // make this loop unnecessary. + // Eventually |active_downloads_| will replace |in_progress_|, but we don't + // want to change the semantics yet. DCHECK(!ContainsKey(in_progress_, info->download_id)); + DownloadItem* download = NULL; + for (std::set<DownloadItem*>::iterator i = downloads_.begin(); + i != downloads_.end(); ++i) { + DownloadItem* item = (*i); + if (item && (item->id() == info->download_id)) { + download = item; + break; + } + } + DCHECK(download != NULL); + download->SetFileCheckResults(info->path, + info->is_dangerous, + info->path_uniquifier, + info->prompt_user_for_save_location, + info->is_extension_install, + info->original_name); in_progress_[info->download_id] = download; bool download_finished = ContainsKey(pending_finished_downloads_, info->download_id); VLOG(20) << __FUNCTION__ << "()" + << " target_path = \"" << target_path.value() << "\"" << " download_finished = " << download_finished - << " info = " << info->DebugString(); + << " info = " << info->DebugString() + << " download = " << download->DebugString(true); if (download_finished || info->is_dangerous) { // The download has already finished or the download is not safe. @@ -454,7 +478,8 @@ void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info, BrowserThread::FILE, FROM_HERE, NewRunnableMethod( file_manager_, &DownloadFileManager::OnFinalDownloadName, - download->id(), target_path, !info->is_dangerous, this)); + download->id(), target_path, !info->is_dangerous, + make_scoped_refptr(this))); } else { // The download hasn't finished and it is a safe download. We need to // rename it to its intermediate '.crdownload' path. @@ -463,7 +488,7 @@ void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info, BrowserThread::FILE, FROM_HERE, NewRunnableMethod( file_manager_, &DownloadFileManager::OnIntermediateDownloadName, - download->id(), download_path, this)); + download->id(), download_path, make_scoped_refptr(this))); download->Rename(download_path); } @@ -537,7 +562,6 @@ void DownloadManager::OnAllDataSaved(int32 download_id, int64 size) { // anything. When the user notifies us, it will trigger a call to // ProceedWithFinishedDangerousDownload. if (download->safety_state() == DownloadItem::DANGEROUS) { - dangerous_finished_[download_id] = download; return; } @@ -567,18 +591,6 @@ void DownloadManager::DownloadRenamedToFinalName(int download_id, item->OnDownloadRenamedToFinalName(full_path); } -void DownloadManager::DownloadFinished(DownloadItem* download) { - VLOG(20) << __FUNCTION__ << "()" - << " download = " << download->DebugString(true); - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // If this was a dangerous download, it has now been approved and must be - // removed from dangerous_finished_ so it does not get deleted on shutdown. - dangerous_finished_.erase(download->id()); - - download->Finished(); -} - // Called on the file thread. Renames the downloaded file to its original name. void DownloadManager::ProceedWithFinishedDangerousDownload( int64 download_handle, @@ -617,8 +629,8 @@ void DownloadManager::DangerousDownloadRenamed(int64 download_handle, << " success = " << success << " new_path = \"" << new_path.value() << "\"" << " new_path_uniquifier = " << new_path_uniquifier; - DownloadMap::iterator it = downloads_.find(download_handle); - if (it == downloads_.end()) { + DownloadMap::iterator it = history_downloads_.find(download_handle); + if (it == history_downloads_.end()) { NOTREACHED(); return; } @@ -633,7 +645,7 @@ void DownloadManager::DangerousDownloadRenamed(int64 download_handle, } // Continue the download finished sequence. - DownloadFinished(download); + download->Finished(); } void DownloadManager::DownloadCancelled(int32 download_id) { @@ -714,8 +726,8 @@ void DownloadManager::PauseDownloadRequest(ResourceDispatcherHost* rdh, } void DownloadManager::RemoveDownload(int64 download_handle) { - DownloadMap::iterator it = downloads_.find(download_handle); - if (it == downloads_.end()) + DownloadMap::iterator it = history_downloads_.find(download_handle); + if (it == history_downloads_.end()) return; // Make history update. @@ -723,10 +735,9 @@ void DownloadManager::RemoveDownload(int64 download_handle) { download_history_->RemoveEntry(download); // Remove from our tables and delete. - downloads_.erase(it); - it = dangerous_finished_.find(download->id()); - if (it != dangerous_finished_.end()) - dangerous_finished_.erase(it); + history_downloads_.erase(it); + int downloads_count = downloads_.erase(download); + DCHECK_EQ(1, downloads_count); // Tell observers to refresh their views. NotifyModelChanged(); @@ -738,9 +749,11 @@ int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin, const base::Time remove_end) { download_history_->RemoveEntriesBetween(remove_begin, remove_end); - DownloadMap::iterator it = downloads_.begin(); + // All downloads visible to the user will be in the history, + // so scan that map. + DownloadMap::iterator it = history_downloads_.begin(); std::vector<DownloadItem*> pending_deletes; - while (it != downloads_.end()) { + while (it != history_downloads_.end()) { DownloadItem* download = it->second; DownloadItem::DownloadState state = download->state(); if (download->start_time() >= remove_begin && @@ -748,13 +761,9 @@ int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin, (state == DownloadItem::COMPLETE || state == DownloadItem::CANCELLED)) { // Remove from the map and move to the next in the list. - downloads_.erase(it++); + history_downloads_.erase(it++); // Also remove it from any completed dangerous downloads. - DownloadMap::iterator dit = dangerous_finished_.find(download->id()); - if (dit != dangerous_finished_.end()) - dangerous_finished_.erase(dit); - pending_deletes.push_back(download); continue; @@ -763,12 +772,22 @@ int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin, ++it; } + // If we aren't deleting anything, we're done. + if (pending_deletes.empty()) + return 0; + + // Remove the chosen downloads from the main owning container. + for (std::vector<DownloadItem*>::iterator it = pending_deletes.begin(); + it != pending_deletes.end(); it++) { + downloads_.erase(*it); + } + // Tell observers to refresh their views. + NotifyModelChanged(); + + // Delete the download items themselves. int num_deleted = static_cast<int>(pending_deletes.size()); - if (num_deleted > 0) - NotifyModelChanged(); - // Delete the download items after updating the observers. STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end()); pending_deletes.clear(); @@ -790,7 +809,10 @@ int DownloadManager::RemoveAllDownloads() { } void DownloadManager::SavePageAsDownloadStarted(DownloadItem* download_item) { - save_page_downloads_.push_back(download_item); +#if !defined(NDEBUG) + save_page_as_downloads_.insert(download_item); +#endif + downloads_.insert(download_item); } // Initiate a download of a specific URL. We send the request to the @@ -836,7 +858,7 @@ bool DownloadManager::ShouldOpenFileBasedOnExtension( FilePath::StringType extension = path.Extension(); if (extension.empty()) return false; - if (download_util::IsExecutableExtension(extension)) + if (!download_util::IsFileExtensionSafe(extension)) return false; if (Extension::IsExtension(path)) return false; @@ -884,7 +906,7 @@ void DownloadManager::FileSelected(const FilePath& path, DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params); if (info->prompt_user_for_save_location) last_download_path_ = path.DirName(); - CreateDownloadItem(info, path); + AttachDownloadItem(info, path); } void DownloadManager::FileSelectionCanceled(void* params) { @@ -922,8 +944,9 @@ void DownloadManager::OnQueryDownloadEntriesComplete( std::vector<DownloadCreateInfo>* entries) { for (size_t i = 0; i < entries->size(); ++i) { DownloadItem* download = new DownloadItem(this, entries->at(i)); - DCHECK(!ContainsKey(downloads_, download->db_handle())); - downloads_[download->db_handle()] = download; + DCHECK(!ContainsKey(history_downloads_, download->db_handle())); + downloads_.insert(download); + history_downloads_[download->db_handle()] = download; VLOG(20) << __FUNCTION__ << "()" << i << ">" << " download = " << download->DebugString(true); } @@ -932,7 +955,7 @@ void DownloadManager::OnQueryDownloadEntriesComplete( // Once the new DownloadItem's creation info has been committed to the history // service, we associate the DownloadItem with the db handle, update our -// 'downloads_' map and inform observers. +// 'history_downloads_' map and inform observers. void DownloadManager::OnCreateDownloadEntryComplete( DownloadCreateInfo info, int64 db_handle) { @@ -956,8 +979,9 @@ void DownloadManager::OnCreateDownloadEntryComplete( download->set_db_handle(db_handle); // Insert into our full map. - DCHECK(downloads_.find(download->db_handle()) == downloads_.end()); - downloads_[download->db_handle()] = download; + DCHECK(history_downloads_.find(download->db_handle()) == + history_downloads_.end()); + history_downloads_[download->db_handle()] = download; // Show in the appropropriate browser UI. ShowDownloadInBrowser(info, download); @@ -1009,8 +1033,8 @@ void DownloadManager::NotifyModelChanged() { } DownloadItem* DownloadManager::GetDownloadItem(int id) { - for (DownloadMap::iterator it = downloads_.begin(); - it != downloads_.end(); ++it) { + for (DownloadMap::iterator it = history_downloads_.begin(); + it != history_downloads_.end(); ++it) { DownloadItem* item = it->second; if (item->id() == id) return item; @@ -1018,6 +1042,51 @@ DownloadItem* DownloadManager::GetDownloadItem(int id) { return NULL; } +// Confirm that everything in all maps is also in |downloads_|, and that +// everything in |downloads_| is also in some other map. +void DownloadManager::AssertContainersConsistent() const { +#if !defined(NDEBUG) + // Turn everything into sets. + DownloadSet in_progress_set, history_set; + const DownloadMap* input_maps[] = {&in_progress_, &history_downloads_}; + DownloadSet* local_sets[] = {&in_progress_set, &history_set}; + DCHECK_EQ(ARRAYSIZE_UNSAFE(input_maps), ARRAYSIZE_UNSAFE(local_sets)); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(input_maps); i++) { + for (DownloadMap::const_iterator it = input_maps[i]->begin(); + it != input_maps[i]->end(); it++) { + local_sets[i]->insert(&*it->second); + } + } + + // Check if each set is fully present in downloads, and create a union. + const DownloadSet* all_sets[] = {&in_progress_set, &history_set, + &save_page_as_downloads_}; + DownloadSet downloads_union; + for (int i = 0; i < static_cast<int>(ARRAYSIZE_UNSAFE(all_sets)); i++) { + DownloadSet remainder; + std::insert_iterator<DownloadSet> insert_it(remainder, remainder.begin()); + std::set_difference(all_sets[i]->begin(), all_sets[i]->end(), + downloads_.begin(), downloads_.end(), + insert_it); + DCHECK(remainder.empty()); + std::insert_iterator<DownloadSet> + insert_union(downloads_union, downloads_union.end()); + std::set_union(downloads_union.begin(), downloads_union.end(), + all_sets[i]->begin(), all_sets[i]->end(), + insert_union); + } + + // Is everything in downloads_ present in one of the other sets? + DownloadSet remainder; + std::insert_iterator<DownloadSet> + insert_remainder(remainder, remainder.begin()); + std::set_difference(downloads_.begin(), downloads_.end(), + downloads_union.begin(), downloads_union.end(), + insert_remainder); + DCHECK(remainder.empty()); +#endif +} + // DownloadManager::OtherDownloadManagerObserver implementation ---------------- DownloadManager::OtherDownloadManagerObserver::OtherDownloadManagerObserver( |