Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4085)

Unified Diff: chrome/browser/download/download_manager.cc

Issue 7618048: Move the core download files to content. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/download/download_manager.cc
===================================================================
--- chrome/browser/download/download_manager.cc (revision 96793)
+++ chrome/browser/download/download_manager.cc (working copy)
@@ -1,1081 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/download/download_manager.h"
-
-#include "base/callback.h"
-#include "base/file_util.h"
-#include "base/i18n/case_conversion.h"
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "base/task.h"
-#include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/download/download_create_info.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_manager_delegate.h"
-#include "chrome/browser/download/download_prefs.h"
-#include "chrome/browser/download/download_request_handle.h"
-#include "chrome/browser/download/download_status_updater.h"
-#include "chrome/browser/download/download_util.h"
-#include "chrome/browser/history/download_history_info.h"
-#include "chrome/browser/profiles/profile.h"
-#include "content/browser/browser_thread.h"
-#include "content/browser/renderer_host/render_process_host.h"
-#include "content/browser/renderer_host/render_view_host.h"
-#include "content/browser/renderer_host/resource_dispatcher_host.h"
-#include "content/browser/tab_contents/tab_contents.h"
-#include "content/common/content_notification_types.h"
-#include "content/common/notification_service.h"
-
-DownloadManager::DownloadManager(DownloadManagerDelegate* delegate,
- DownloadStatusUpdater* status_updater)
- : shutdown_needed_(false),
- profile_(NULL),
- file_manager_(NULL),
- status_updater_(status_updater->AsWeakPtr()),
- next_save_page_id_(0),
- delegate_(delegate) {
- if (status_updater_)
- status_updater_->AddDelegate(this);
-}
-
-DownloadManager::~DownloadManager() {
- DCHECK(!shutdown_needed_);
- if (status_updater_)
- status_updater_->RemoveDelegate(this);
-}
-
-void DownloadManager::Shutdown() {
- VLOG(20) << __FUNCTION__ << "()"
- << " shutdown_needed_ = " << shutdown_needed_;
- if (!shutdown_needed_)
- return;
- shutdown_needed_ = false;
-
- FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown());
-
- if (file_manager_) {
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(file_manager_,
- &DownloadFileManager::OnDownloadManagerShutdown,
- make_scoped_refptr(this)));
- }
-
- 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->IsPartialDownload()) {
- // 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->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN);
- } else if (download->IsPartialDownload()) {
- download->Cancel(false);
- download_history_->UpdateEntry(download);
- }
- }
-
- // 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.
-
- // Copy downloads_ to separate container so as not to set off checks
- // in DownloadItem destruction.
- DownloadSet downloads_to_delete;
- downloads_to_delete.swap(downloads_);
-
- in_progress_.clear();
- active_downloads_.clear();
- history_downloads_.clear();
- STLDeleteElements(&downloads_to_delete);
-
- DCHECK(save_page_downloads_.empty());
-
- file_manager_ = NULL;
-
- download_history_.reset();
- download_prefs_.reset();
-
- shutdown_needed_ = false;
-}
-
-void DownloadManager::GetTemporaryDownloads(
- const FilePath& dir_path, DownloadVector* result) {
- DCHECK(result);
-
- 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);
- }
-}
-
-void DownloadManager::GetAllDownloads(
- const FilePath& dir_path, DownloadVector* result) {
- DCHECK(result);
-
- 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);
- }
-}
-
-void DownloadManager::GetCurrentDownloads(
- const FilePath& dir_path, DownloadVector* result) {
- DCHECK(result);
-
- for (DownloadMap::iterator it = history_downloads_.begin();
- it != history_downloads_.end(); ++it) {
- DownloadItem* item =it->second;
- // Skip temporary items.
- if (item->is_temporary())
- continue;
- // Skip items that have all their data, and are OK to save.
- if (!item->IsPartialDownload() &&
- (item->safety_state() != DownloadItem::DANGEROUS))
- continue;
- // Skip items that don't match |dir_path|.
- // If |dir_path| is empty, all remaining items match.
- if (!dir_path.empty() && (it->second->full_path().DirName() != dir_path))
- continue;
-
- result->push_back(item);
- }
-
- // If we have a parent profile, let it add its downloads to the results.
- Profile* original_profile = profile_->GetOriginalProfile();
- if (original_profile != profile_)
- original_profile->GetDownloadManager()->GetCurrentDownloads(dir_path,
- result);
-}
-
-void DownloadManager::SearchDownloads(const string16& query,
- DownloadVector* result) {
- DCHECK(result);
-
- string16 query_lower(base::i18n::ToLower(query));
-
- 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())
- continue;
-
- // Display Incognito downloads only in Incognito window, and vice versa.
- // The Incognito Downloads page will get the list of non-Incognito downloads
- // from its parent profile.
- if (profile_->IsOffTheRecord() != download_item->is_otr())
- continue;
-
- if (download_item->MatchesQuery(query_lower))
- result->push_back(download_item);
- }
-
- // If we have a parent profile, let it add its downloads to the results.
- Profile* original_profile = profile_->GetOriginalProfile();
- if (original_profile != profile_)
- original_profile->GetDownloadManager()->SearchDownloads(query, result);
-}
-
-// Query the history service for information about all persisted downloads.
-bool DownloadManager::Init(Profile* profile) {
- DCHECK(profile);
- DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
- shutdown_needed_ = true;
-
- profile_ = profile;
- download_history_.reset(new DownloadHistory(profile));
- download_history_->Load(
- NewCallback(this, &DownloadManager::OnQueryDownloadEntriesComplete));
-
- download_prefs_.reset(new DownloadPrefs(profile_->GetPrefs()));
-
- // In test mode, there may be no ResourceDispatcherHost. In this case it's
- // safe to avoid setting |file_manager_| because we only call a small set of
- // functions, none of which need it.
- ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
- if (rdh) {
- file_manager_ = rdh->download_file_manager();
- DCHECK(file_manager_);
- }
-
- other_download_manager_observer_.reset(
- new OtherDownloadManagerObserver(this));
-
- return true;
-}
-
-// We have received a message from DownloadFileManager about a new download.
-void DownloadManager::StartDownload(int32 download_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (delegate_->ShouldStartDownload(download_id))
- RestartDownload(download_id);
-}
-
-void DownloadManager::CheckForHistoryFilesRemoval() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- for (DownloadMap::iterator it = history_downloads_.begin();
- it != history_downloads_.end(); ++it) {
- CheckForFileRemoval(it->second);
- }
-}
-
-void DownloadManager::CheckForFileRemoval(DownloadItem* download_item) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (download_item->IsComplete() &&
- !download_item->file_externally_removed()) {
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(this,
- &DownloadManager::CheckForFileRemovalOnFileThread,
- download_item->db_handle(),
- download_item->GetTargetFilePath()));
- }
-}
-
-void DownloadManager::CheckForFileRemovalOnFileThread(
- int64 db_handle, const FilePath& path) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- if (!file_util::PathExists(path)) {
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(this,
- &DownloadManager::OnFileRemovalDetected,
- db_handle));
- }
-}
-
-void DownloadManager::OnFileRemovalDetected(int64 db_handle) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DownloadMap::iterator it = history_downloads_.find(db_handle);
- if (it != history_downloads_.end()) {
- DownloadItem* download_item = it->second;
- download_item->OnDownloadedFileRemoved();
- }
-}
-
-void DownloadManager::RestartDownload(
- int32 download_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- DownloadItem* download = GetActiveDownloadItem(download_id);
- if (!download)
- return;
-
- VLOG(20) << __FUNCTION__ << "()"
- << " download = " << download->DebugString(true);
-
- FilePath suggested_path = download->suggested_path();
-
- if (download->prompt_user_for_save_location()) {
- // We must ask the user for the place to put the download.
- DownloadRequestHandle request_handle = download->request_handle();
- TabContents* contents = request_handle.GetTabContents();
-
- // |id_ptr| will be deleted in either FileSelected() or
- // FileSelectionCancelled().
- int32* id_ptr = new int32;
- *id_ptr = download_id;
-
- delegate_->ChooseDownloadPath(
- contents, suggested_path, reinterpret_cast<void*>(id_ptr));
-
- FOR_EACH_OBSERVER(Observer, observers_,
- SelectFileDialogDisplayed(download_id));
- } else {
- // No prompting for download, just continue with the suggested name.
- ContinueDownloadWithPath(download, suggested_path);
- }
-}
-
-void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- DownloadItem* download = new DownloadItem(this, *info,
- profile_->IsOffTheRecord());
- int32 download_id = info->download_id;
- DCHECK(!ContainsKey(in_progress_, download_id));
- DCHECK(!ContainsKey(active_downloads_, download_id));
- downloads_.insert(download);
- active_downloads_[download_id] = download;
-}
-
-void DownloadManager::ContinueDownloadWithPath(DownloadItem* download,
- const FilePath& chosen_file) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(download);
-
- int32 download_id = download->id();
-
- // NOTE(ahendrickson) Eventually |active_downloads_| will replace
- // |in_progress_|, but we don't want to change the semantics yet.
- DCHECK(!ContainsKey(in_progress_, download_id));
- DCHECK(ContainsKey(downloads_, download));
- DCHECK(ContainsKey(active_downloads_, download_id));
-
- // Make sure the initial file name is set only once.
- DCHECK(download->full_path().empty());
- download->OnPathDetermined(chosen_file);
-
- VLOG(20) << __FUNCTION__ << "()"
- << " download = " << download->DebugString(true);
-
- in_progress_[download_id] = download;
- UpdateAppIcon(); // Reflect entry into in_progress_.
-
- // Rename to intermediate name.
- FilePath download_path;
- if (download->IsDangerous()) {
- // The download is not safe. We can now rename the file to its
- // tentative name using RenameInProgressDownloadFile.
- // NOTE: The |Rename| below will be a no-op for dangerous files, as we're
- // renaming it to the same name.
- download_path = download->full_path();
- } else {
- // The download is a safe download. We need to
- // rename it to its intermediate '.crdownload' path. The final
- // name after user confirmation will be set from
- // DownloadItem::OnDownloadCompleting.
- download_path =
- download_util::GetCrDownloadPath(download->full_path());
- }
-
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(
- file_manager_, &DownloadFileManager::RenameInProgressDownloadFile,
- download->id(), download_path));
-
- download->Rename(download_path);
-
- download_history_->AddEntry(download,
- NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete));
-}
-
-void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DownloadMap::iterator it = active_downloads_.find(download_id);
- if (it != active_downloads_.end()) {
- DownloadItem* download = it->second;
- if (download->IsInProgress()) {
- download->Update(size);
- UpdateAppIcon(); // Reflect size updates.
- download_history_->UpdateEntry(download);
- }
- }
-}
-
-void DownloadManager::OnResponseCompleted(int32 download_id,
- int64 size,
- int os_error,
- const std::string& hash) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- // ERR_CONNECTION_CLOSED is allowed since a number of servers in the wild
- // advertise a larger Content-Length than the amount of bytes in the message
- // body, and then close the connection. Other browsers - IE8, Firefox 4.0.1,
- // and Safari 5.0.4 - treat the download as complete in this case, so we
- // follow their lead.
- if (os_error == 0 || os_error == net::ERR_CONNECTION_CLOSED) {
- OnAllDataSaved(download_id, size, hash);
- } else {
- OnDownloadError(download_id, size, os_error);
- }
-}
-
-void DownloadManager::OnAllDataSaved(int32 download_id,
- int64 size,
- const std::string& hash) {
- VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
- << " size = " << size;
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- // If it's not in active_downloads_, that means it was cancelled; just
- // ignore the notification.
- if (active_downloads_.count(download_id) == 0)
- return;
-
- DownloadItem* download = active_downloads_[download_id];
- download->OnAllDataSaved(size);
-
- MaybeCompleteDownload(download);
-}
-
-void DownloadManager::AssertQueueStateConsistent(DownloadItem* download) {
- // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
- if (download->state() == DownloadItem::REMOVING) {
- CHECK(!ContainsKey(downloads_, download));
- CHECK(!ContainsKey(active_downloads_, download->id()));
- CHECK(!ContainsKey(in_progress_, download->id()));
- CHECK(!ContainsKey(history_downloads_, download->db_handle()));
- return;
- }
-
- // Should be in downloads_ if we're not REMOVING.
- CHECK(ContainsKey(downloads_, download));
-
- // Check history_downloads_ consistency.
- if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
- CHECK(ContainsKey(history_downloads_, download->db_handle()));
- } else {
- // TODO(rdsmith): Somewhat painful; make sure to disable in
- // release builds after resolution of http://crbug.com/85408.
- for (DownloadMap::iterator it = history_downloads_.begin();
- it != history_downloads_.end(); ++it) {
- CHECK(it->second != download);
- }
- }
-
- CHECK(ContainsKey(active_downloads_, download->id()) ==
- (download->state() == DownloadItem::IN_PROGRESS));
- CHECK(ContainsKey(in_progress_, download->id()) ==
- (download->state() == DownloadItem::IN_PROGRESS));
-}
-
-bool DownloadManager::IsDownloadReadyForCompletion(DownloadItem* download) {
- // If we don't have all the data, the download is not ready for
- // completion.
- if (!download->all_data_saved())
- return false;
-
- // If the download is dangerous, but not yet validated, it's not ready for
- // completion.
- if (download->safety_state() == DownloadItem::DANGEROUS)
- return false;
-
- // If the download isn't active (e.g. has been cancelled) it's not
- // ready for completion.
- if (active_downloads_.count(download->id()) == 0)
- return false;
-
- // If the download hasn't been inserted into the history system
- // (which occurs strictly after file name determination, intermediate
- // file rename, and UI display) then it's not ready for completion.
- if (download->db_handle() == DownloadHistory::kUninitializedHandle)
- return false;
-
- return true;
-}
-
-void DownloadManager::MaybeCompleteDownload(DownloadItem* download) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- VLOG(20) << __FUNCTION__ << "()" << " download = "
- << download->DebugString(false);
-
- if (!IsDownloadReadyForCompletion(download))
- return;
-
- // TODO(rdsmith): DCHECK that we only pass through this point
- // once per download. The natural way to do this is by a state
- // transition on the DownloadItem.
-
- // Confirm we're in the proper set of states to be here;
- // in in_progress_, have all data, have a history handle, (validated or safe).
- DCHECK_NE(DownloadItem::DANGEROUS, download->safety_state());
- DCHECK_EQ(1u, in_progress_.count(download->id()));
- DCHECK(download->all_data_saved());
- DCHECK(download->db_handle() != DownloadHistory::kUninitializedHandle);
- DCHECK_EQ(1u, history_downloads_.count(download->db_handle()));
-
- VLOG(20) << __FUNCTION__ << "()" << " executing: download = "
- << download->DebugString(false);
-
- // Remove the id from in_progress
- in_progress_.erase(download->id());
- UpdateAppIcon(); // Reflect removal from in_progress_.
-
- download_history_->UpdateEntry(download);
-
- // Finish the download.
- download->OnDownloadCompleting(file_manager_);
-}
-
-void DownloadManager::DownloadCompleted(int32 download_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DownloadItem* download = GetDownloadItem(download_id);
- DCHECK(download);
- download_history_->UpdateEntry(download);
- active_downloads_.erase(download_id);
-}
-
-void DownloadManager::OnDownloadRenamedToFinalName(int download_id,
- const FilePath& full_path,
- int uniquifier) {
- VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
- << " full_path = \"" << full_path.value() << "\""
- << " uniquifier = " << uniquifier;
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- DownloadItem* item = GetDownloadItem(download_id);
- if (!item)
- return;
-
- if (item->safety_state() == DownloadItem::SAFE) {
- DCHECK_EQ(0, uniquifier) << "We should not uniquify SAFE downloads twice";
- }
-
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(
- file_manager_, &DownloadFileManager::CompleteDownload, download_id));
-
- if (uniquifier)
- item->set_path_uniquifier(uniquifier);
-
- item->OnDownloadRenamedToFinalName(full_path);
- download_history_->UpdateDownloadPath(item, full_path);
-}
-
-void DownloadManager::DownloadCancelled(int32 download_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DownloadMap::iterator it = in_progress_.find(download_id);
- if (it == in_progress_.end())
- return;
- DownloadItem* download = it->second;
-
- VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
- << " download = " << download->DebugString(true);
-
- // Clean up will happen when the history system create callback runs if we
- // don't have a valid db_handle yet.
- if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
- in_progress_.erase(it);
- active_downloads_.erase(download_id);
- UpdateAppIcon(); // Reflect removal from in_progress_.
- download_history_->UpdateEntry(download);
- }
-
- DownloadCancelledInternal(download_id, download->request_handle());
-}
-
-void DownloadManager::DownloadCancelledInternal(
- int download_id, const DownloadRequestHandle& request_handle) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- request_handle.CancelRequest();
-
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(
- file_manager_, &DownloadFileManager::CancelDownload, download_id));
-}
-
-void DownloadManager::OnDownloadError(int32 download_id,
- int64 size,
- int os_error) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DownloadMap::iterator it = active_downloads_.find(download_id);
- // A cancel at the right time could remove the download from the
- // |active_downloads_| map before we get here.
- if (it == active_downloads_.end())
- return;
-
- DownloadItem* download = it->second;
-
- VLOG(20) << __FUNCTION__ << "()" << " Error " << os_error
- << " at offset " << download->received_bytes()
- << " for download = " << download->DebugString(true);
-
- download->Interrupted(size, os_error);
-
- // TODO(ahendrickson) - Remove this when we add resuming of interrupted
- // downloads, as we will keep the download item around in that case.
- //
- // Clean up will happen when the history system create callback runs if we
- // don't have a valid db_handle yet.
- if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
- in_progress_.erase(download_id);
- active_downloads_.erase(download_id);
- UpdateAppIcon(); // Reflect removal from in_progress_.
- download_history_->UpdateEntry(download);
- }
-
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(
- file_manager_, &DownloadFileManager::CancelDownload, download_id));
-}
-
-void DownloadManager::UpdateAppIcon() {
- if (status_updater_)
- status_updater_->Update();
-}
-
-int DownloadManager::RemoveDownloadItems(
- const DownloadVector& pending_deletes) {
- if (pending_deletes.empty())
- return 0;
-
- // Delete from internal maps.
- for (DownloadVector::const_iterator it = pending_deletes.begin();
- it != pending_deletes.end();
- ++it) {
- DownloadItem* download = *it;
- DCHECK(download);
- history_downloads_.erase(download->db_handle());
- save_page_downloads_.erase(download->id());
- downloads_.erase(download);
- }
-
- // Tell observers to refresh their views.
- NotifyModelChanged();
-
- // Delete the download items themselves.
- const int num_deleted = static_cast<int>(pending_deletes.size());
- STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end());
- return num_deleted;
-}
-
-void DownloadManager::RemoveDownload(int64 download_handle) {
- DownloadMap::iterator it = history_downloads_.find(download_handle);
- if (it == history_downloads_.end())
- return;
-
- // Make history update.
- DownloadItem* download = it->second;
- download_history_->RemoveEntry(download);
-
- // Remove from our tables and delete.
- int downloads_count = RemoveDownloadItems(DownloadVector(1, download));
- DCHECK_EQ(1, downloads_count);
-}
-
-int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin,
- const base::Time remove_end) {
- download_history_->RemoveEntriesBetween(remove_begin, remove_end);
-
- // All downloads visible to the user will be in the history,
- // so scan that map.
- DownloadVector pending_deletes;
- for (DownloadMap::const_iterator it = history_downloads_.begin();
- it != history_downloads_.end();
- ++it) {
- DownloadItem* download = it->second;
- if (download->start_time() >= remove_begin &&
- (remove_end.is_null() || download->start_time() < remove_end) &&
- (download->IsComplete() || download->IsCancelled())) {
- AssertQueueStateConsistent(download);
- pending_deletes.push_back(download);
- }
- }
- return RemoveDownloadItems(pending_deletes);
-}
-
-int DownloadManager::RemoveDownloads(const base::Time remove_begin) {
- return RemoveDownloadsBetween(remove_begin, base::Time());
-}
-
-int DownloadManager::RemoveAllDownloads() {
- if (this != profile_->GetOriginalProfile()->GetDownloadManager()) {
- // This is an incognito downloader. Clear All should clear main download
- // manager as well.
- profile_->GetOriginalProfile()->GetDownloadManager()->RemoveAllDownloads();
- }
- // The null times make the date range unbounded.
- return RemoveDownloadsBetween(base::Time(), base::Time());
-}
-
-// Initiate a download of a specific URL. We send the request to the
-// ResourceDispatcherHost, and let it send us responses like a regular
-// download.
-void DownloadManager::DownloadUrl(const GURL& url,
- const GURL& referrer,
- const std::string& referrer_charset,
- TabContents* tab_contents) {
- DownloadUrlToFile(url, referrer, referrer_charset, DownloadSaveInfo(),
- tab_contents);
-}
-
-void DownloadManager::DownloadUrlToFile(const GURL& url,
- const GURL& referrer,
- const std::string& referrer_charset,
- const DownloadSaveInfo& save_info,
- TabContents* tab_contents) {
- DCHECK(tab_contents);
- // We send a pointer to content::ResourceContext, instead of the usual
- // reference, so that a copy of the object isn't made.
- BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
- NewRunnableFunction(&download_util::DownloadUrl,
- url,
- referrer,
- referrer_charset,
- save_info,
- g_browser_process->resource_dispatcher_host(),
- tab_contents->GetRenderProcessHost()->id(),
- tab_contents->render_view_host()->routing_id(),
- &tab_contents->browser_context()->
- GetResourceContext()));
-}
-
-void DownloadManager::AddObserver(Observer* observer) {
- observers_.AddObserver(observer);
- observer->ModelChanged();
-}
-
-void DownloadManager::RemoveObserver(Observer* observer) {
- observers_.RemoveObserver(observer);
-}
-
-bool DownloadManager::IsDownloadProgressKnown() {
- for (DownloadMap::iterator i = in_progress_.begin();
- i != in_progress_.end(); ++i) {
- if (i->second->total_bytes() <= 0)
- return false;
- }
-
- return true;
-}
-
-int64 DownloadManager::GetInProgressDownloadCount() {
- return in_progress_.size();
-}
-
-int64 DownloadManager::GetReceivedDownloadBytes() {
- DCHECK(IsDownloadProgressKnown());
- int64 received_bytes = 0;
- for (DownloadMap::iterator i = in_progress_.begin();
- i != in_progress_.end(); ++i) {
- received_bytes += i->second->received_bytes();
- }
- return received_bytes;
-}
-
-int64 DownloadManager::GetTotalDownloadBytes() {
- DCHECK(IsDownloadProgressKnown());
- int64 total_bytes = 0;
- for (DownloadMap::iterator i = in_progress_.begin();
- i != in_progress_.end(); ++i) {
- total_bytes += i->second->total_bytes();
- }
- return total_bytes;
-}
-
-void DownloadManager::FileSelected(const FilePath& path, void* params) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- int32* id_ptr = reinterpret_cast<int32*>(params);
- DCHECK(id_ptr != NULL);
- int32 download_id = *id_ptr;
- delete id_ptr;
-
- DownloadItem* download = GetActiveDownloadItem(download_id);
- if (!download)
- return;
- VLOG(20) << __FUNCTION__ << "()" << " path = \"" << path.value() << "\""
- << " download = " << download->DebugString(true);
-
- if (download->prompt_user_for_save_location())
- last_download_path_ = path.DirName();
-
- // Make sure the initial file name is set only once.
- ContinueDownloadWithPath(download, path);
-}
-
-void DownloadManager::FileSelectionCanceled(void* params) {
- // The user didn't pick a place to save the file, so need to cancel the
- // download that's already in progress to the temporary location.
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- int32* id_ptr = reinterpret_cast<int32*>(params);
- DCHECK(id_ptr != NULL);
- int32 download_id = *id_ptr;
- delete id_ptr;
-
- DownloadItem* download = GetActiveDownloadItem(download_id);
- if (!download)
- return;
-
- VLOG(20) << __FUNCTION__ << "()"
- << " download = " << download->DebugString(true);
-
- DownloadCancelledInternal(download_id, download->request_handle());
-}
-
-// Operations posted to us from the history service ----------------------------
-
-// The history service has retrieved all download entries. 'entries' contains
-// 'DownloadHistoryInfo's in sorted order (by ascending start_time).
-void DownloadManager::OnQueryDownloadEntriesComplete(
- std::vector<DownloadHistoryInfo>* entries) {
- for (size_t i = 0; i < entries->size(); ++i) {
- DownloadItem* download = new DownloadItem(this, entries->at(i));
- DCHECK(!ContainsKey(history_downloads_, download->db_handle()));
- downloads_.insert(download);
- history_downloads_[download->db_handle()] = download;
- VLOG(20) << __FUNCTION__ << "()" << i << ">"
- << " download = " << download->DebugString(true);
- }
- NotifyModelChanged();
- CheckForHistoryFilesRemoval();
-}
-
-void DownloadManager::AddDownloadItemToHistory(DownloadItem* download,
- int64 db_handle) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- // It's not immediately obvious, but HistoryBackend::CreateDownload() can
- // call this function with an invalid |db_handle|. For instance, this can
- // happen when the history database is offline. We cannot have multiple
- // DownloadItems with the same invalid db_handle, so we need to assign a
- // unique |db_handle| here.
- if (db_handle == DownloadHistory::kUninitializedHandle)
- db_handle = download_history_->GetNextFakeDbHandle();
-
- // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/84508
- // is fixed.
- CHECK_NE(DownloadHistory::kUninitializedHandle, db_handle);
-
- DCHECK(download->db_handle() == DownloadHistory::kUninitializedHandle);
- download->set_db_handle(db_handle);
-
- DCHECK(!ContainsKey(history_downloads_, download->db_handle()));
- history_downloads_[download->db_handle()] = download;
-
- // Show in the appropriate browser UI.
- // This includes buttons to save or cancel, for a dangerous download.
- ShowDownloadInBrowser(download);
-
- // Inform interested objects about the new download.
- NotifyModelChanged();
-}
-
-// Once the new DownloadItem's creation info has been committed to the history
-// service, we associate the DownloadItem with the db handle, update our
-// 'history_downloads_' map and inform observers.
-void DownloadManager::OnCreateDownloadEntryComplete(int32 download_id,
- int64 db_handle) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DownloadItem* download = GetActiveDownloadItem(download_id);
- if (!download)
- return;
-
- VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle
- << " download_id = " << download_id
- << " download = " << download->DebugString(true);
-
- AddDownloadItemToHistory(download, db_handle);
-
- // If the download is still in progress, try to complete it.
- //
- // Otherwise, download has been cancelled or interrupted before we've
- // received the DB handle. We post one final message to the history
- // service so that it can be properly in sync with the DownloadItem's
- // completion status, and also inform any observers so that they get
- // more than just the start notification.
- if (download->IsInProgress()) {
- MaybeCompleteDownload(download);
- } else {
- DCHECK(download->IsCancelled())
- << " download = " << download->DebugString(true);
- in_progress_.erase(download_id);
- active_downloads_.erase(download_id);
- download_history_->UpdateEntry(download);
- download->UpdateObservers();
- }
-}
-
-void DownloadManager::ShowDownloadInBrowser(DownloadItem* download) {
- // The 'contents' may no longer exist if the user closed the tab before we
- // get this start completion event.
- DownloadRequestHandle request_handle = download->request_handle();
- TabContents* content = request_handle.GetTabContents();
-
- // If the contents no longer exists, we ask the embedder to suggest another
- // tab.
- if (!content)
- content = delegate_->GetAlternativeTabContentsToNotifyForDownload();
-
- if (content)
- content->OnStartDownload(download);
-}
-
-// Clears the last download path, used to initialize "save as" dialogs.
-void DownloadManager::ClearLastDownloadPath() {
- last_download_path_ = FilePath();
-}
-
-void DownloadManager::NotifyModelChanged() {
- FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
-}
-
-DownloadItem* DownloadManager::GetDownloadItem(int download_id) {
- // The |history_downloads_| map is indexed by the download's db_handle,
- // not its id, so we have to iterate.
- for (DownloadMap::iterator it = history_downloads_.begin();
- it != history_downloads_.end(); ++it) {
- DownloadItem* item = it->second;
- if (item->id() == download_id)
- return item;
- }
- return NULL;
-}
-
-DownloadItem* DownloadManager::GetActiveDownloadItem(int download_id) {
- DCHECK(ContainsKey(active_downloads_, download_id));
- DownloadItem* download = active_downloads_[download_id];
- DCHECK(download != NULL);
- return download;
-}
-
-// 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.
- const DownloadMap* input_maps[] = {&active_downloads_,
- &history_downloads_,
- &save_page_downloads_};
- DownloadSet active_set, history_set, save_page_set;
- DownloadSet* all_sets[] = {&active_set, &history_set, &save_page_set};
- DCHECK_EQ(ARRAYSIZE_UNSAFE(input_maps), ARRAYSIZE_UNSAFE(all_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) {
- all_sets[i]->insert(&*it->second);
- }
- }
-
- // Check if each set is fully present in downloads, and create a union.
- 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(
- DownloadManager* observing_download_manager)
- : observing_download_manager_(observing_download_manager),
- observed_download_manager_(NULL) {
- if (observing_download_manager->profile_->GetOriginalProfile() ==
- observing_download_manager->profile_) {
- return;
- }
-
- observed_download_manager_ = observing_download_manager_->
- profile_->GetOriginalProfile()->GetDownloadManager();
- observed_download_manager_->AddObserver(this);
-}
-
-DownloadManager::OtherDownloadManagerObserver::~OtherDownloadManagerObserver() {
- if (observed_download_manager_)
- observed_download_manager_->RemoveObserver(this);
-}
-
-void DownloadManager::OtherDownloadManagerObserver::ModelChanged() {
- observing_download_manager_->NotifyModelChanged();
-}
-
-void DownloadManager::OtherDownloadManagerObserver::ManagerGoingDown() {
- observed_download_manager_ = NULL;
-}
-
-void DownloadManager::SavePageDownloadStarted(DownloadItem* download) {
- DCHECK(!ContainsKey(save_page_downloads_, download->id()));
- downloads_.insert(download);
- save_page_downloads_[download->id()] = download;
-
- // Add this entry to the history service.
- // Additionally, the UI is notified in the callback.
- download_history_->AddEntry(download,
- NewCallback(this, &DownloadManager::OnSavePageDownloadEntryAdded));
-}
-
-// SavePackage will call SavePageDownloadFinished upon completion/cancellation.
-// The history callback will call OnSavePageDownloadEntryAdded.
-// If the download finishes before the history callback,
-// OnSavePageDownloadEntryAdded calls SavePageDownloadFinished, ensuring that
-// the history event is update regardless of the order in which these two events
-// complete.
-// If something removes the download item from the download manager (Remove,
-// Shutdown) the result will be that the SavePage system will not be able to
-// properly update the download item (which no longer exists) or the download
-// history, but the action will complete properly anyway. This may lead to the
-// history entry being wrong on a reload of chrome (specifically in the case of
-// Initiation -> History Callback -> Removal -> Completion), but there's no way
-// to solve that without canceling on Remove (which would then update the DB).
-
-void DownloadManager::OnSavePageDownloadEntryAdded(int32 download_id,
- int64 db_handle) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- DownloadMap::const_iterator it = save_page_downloads_.find(download_id);
- // This can happen if the download manager is shutting down and all maps
- // have been cleared.
- if (it == save_page_downloads_.end())
- return;
-
- DownloadItem* download = it->second;
- if (!download) {
- NOTREACHED();
- return;
- }
-
- AddDownloadItemToHistory(download, db_handle);
-
- // Finalize this download if it finished before the history callback.
- if (!download->IsInProgress())
- SavePageDownloadFinished(download);
-}
-
-void DownloadManager::SavePageDownloadFinished(DownloadItem* download) {
- if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
- download_history_->UpdateEntry(download);
- DCHECK(ContainsKey(save_page_downloads_, download->id()));
- save_page_downloads_.erase(download->id());
-
- if (download->IsComplete())
- NotificationService::current()->Notify(
- content::NOTIFICATION_SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
- Source<DownloadManager>(this),
- Details<DownloadItem>(download));
- }
-}
-
-int32 DownloadManager::GetNextSavePageId() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- return next_save_page_id_++;
-}
-

Powered by Google App Engine
This is Rietveld 408576698