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

Unified Diff: chrome/browser/download_manager.cc

Issue 2826: Move the download code to new directories: (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 12 years, 3 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
« no previous file with comments | « chrome/browser/download_manager.h ('k') | chrome/browser/download_manager_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/download_manager.cc
===================================================================
--- chrome/browser/download_manager.cc (revision 2162)
+++ chrome/browser/download_manager.cc (working copy)
@@ -1,1104 +0,0 @@
-// Copyright (c) 2006-2008 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 <time.h>
-
-#include "chrome/browser/download_manager.h"
-
-#include "base/file_util.h"
-#include "base/logging.h"
-#include "base/message_loop.h"
-#include "base/path_service.h"
-#include "base/registry.h"
-#include "base/string_util.h"
-#include "base/task.h"
-#include "base/thread.h"
-#include "base/timer.h"
-#include "base/win_util.h"
-#include "chrome/browser/browser_list.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/download_file.h"
-#include "chrome/browser/download_util.h"
-#include "chrome/browser/profile.h"
-#include "chrome/browser/render_process_host.h"
-#include "chrome/browser/render_view_host.h"
-#include "chrome/browser/resource_dispatcher_host.h"
-#include "chrome/browser/tab_util.h"
-#include "chrome/browser/web_contents.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/l10n_util.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/pref_service.h"
-#include "chrome/common/stl_util-inl.h"
-#include "chrome/common/win_util.h"
-#include "googleurl/src/gurl.h"
-#include "net/base/mime_util.h"
-#include "net/base/net_util.h"
-#include "net/url_request/url_request_context.h"
-
-#include "generated_resources.h"
-
-// Periodically update our observers.
-class DownloadItemUpdateTask : public Task {
- public:
- explicit DownloadItemUpdateTask(DownloadItem* item) : item_(item) {}
- void Run() { if (item_) item_->UpdateObservers(); }
-
- private:
- DownloadItem* item_;
-};
-
-// Update frequency (milliseconds).
-static const int kUpdateTimeMs = 1000;
-
-// Our download table ID starts at 1, so we use 0 to represent a download that
-// has started, but has not yet had its data persisted in the table. We use fake
-// database handles in incognito mode starting at -1 and progressly getting more
-// negative.
-static const int kUninitializedHandle = 0;
-
-// Attempts to modify |path| to be a non-existing path.
-// Returns true if |path| points to a non-existing path upon return.
-static bool UniquifyPath(std::wstring* path) {
- DCHECK(path);
- const int kMaxAttempts = 100;
-
- if (!file_util::PathExists(*path))
- return true;
-
- std::wstring new_path;
- for (int count = 1; count <= kMaxAttempts; ++count) {
- new_path.assign(*path);
- file_util::InsertBeforeExtension(&new_path, StringPrintf(L" (%d)", count));
-
- if (!file_util::PathExists(new_path)) {
- path->swap(new_path);
- return true;
- }
- }
-
- return false;
-}
-
-static bool DownloadPathIsDangerous(const std::wstring& download_path) {
- std::wstring desktop_dir;
- if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_dir)) {
- NOTREACHED();
- return false;
- }
- return (download_path == desktop_dir);
-}
-
-// DownloadItem implementation -------------------------------------------------
-
-// Constructor for reading from the history service.
-DownloadItem::DownloadItem(const DownloadCreateInfo& info)
- : id_(-1),
- full_path_(info.path),
- url_(info.url),
- total_bytes_(info.total_bytes),
- received_bytes_(info.received_bytes),
- start_tick_(0),
- state_(static_cast<DownloadState>(info.state)),
- start_time_(info.start_time),
- db_handle_(info.db_handle),
- manager_(NULL),
- is_paused_(false),
- open_when_complete_(false),
- render_process_id_(-1),
- request_id_(-1) {
- if (state_ == IN_PROGRESS)
- state_ = CANCELLED;
- Init(false /* don't start progress timer */);
-}
-
-// Constructor for DownloadItem created via user action in the main thread.
-DownloadItem::DownloadItem(int32 download_id,
- const std::wstring& path,
- const std::wstring& url,
- const Time start_time,
- int64 download_size,
- int render_process_id,
- int request_id)
- : id_(download_id),
- full_path_(path),
- url_(url),
- total_bytes_(download_size),
- received_bytes_(0),
- start_tick_(GetTickCount()),
- state_(IN_PROGRESS),
- start_time_(start_time),
- db_handle_(kUninitializedHandle),
- manager_(NULL),
- is_paused_(false),
- open_when_complete_(false),
- render_process_id_(render_process_id),
- request_id_(request_id) {
- Init(true /* start progress timer */);
-}
-
-void DownloadItem::Init(bool start_timer) {
- file_name_ = file_util::GetFilenameFromPath(full_path_);
- if (start_timer)
- StartProgressTimer();
-}
-
-DownloadItem::~DownloadItem() {
- state_ = REMOVING;
- UpdateObservers();
-}
-
-void DownloadItem::AddObserver(Observer* observer) {
- observers_.AddObserver(observer);
-}
-
-void DownloadItem::RemoveObserver(Observer* observer) {
- observers_.RemoveObserver(observer);
-}
-
-void DownloadItem::UpdateObservers() {
- FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
-}
-
-// If we've received more data than we were expecting (bad server info?), revert
-// to 'unknown size mode'.
-void DownloadItem::UpdateSize(int64 bytes_so_far) {
- received_bytes_ = bytes_so_far;
- if (received_bytes_ > total_bytes_)
- total_bytes_ = 0;
-}
-
-// Updates from the download thread may have been posted while this download
-// was being cancelled in the UI thread, so we'll accept them unless we're
-// complete.
-void DownloadItem::Update(int64 bytes_so_far) {
- if (state_ == COMPLETE) {
- NOTREACHED();
- return;
- }
- UpdateSize(bytes_so_far);
- UpdateObservers();
-}
-
-// Triggered by a user action
-void DownloadItem::Cancel(bool update_history) {
- if (state_ != IN_PROGRESS) {
- // Small downloads might be complete before this method has a chance to run.
- return;
- }
- state_ = CANCELLED;
- UpdateObservers();
- StopProgressTimer();
- if (update_history)
- manager_->DownloadCancelled(id_);
-}
-
-void DownloadItem::Finished(int64 size) {
- state_ = COMPLETE;
- UpdateSize(size);
- UpdateObservers();
- StopProgressTimer();
-}
-
-void DownloadItem::Remove() {
- Cancel(true);
- state_ = REMOVING;
- manager_->RemoveDownload(db_handle_);
-}
-
-void DownloadItem::StartProgressTimer() {
- update_timer_.Start(TimeDelta::FromMilliseconds(kUpdateTimeMs), this,
- &DownloadItem::UpdateObservers);
-}
-
-void DownloadItem::StopProgressTimer() {
- update_timer_.Stop();
-}
-
-bool DownloadItem::TimeRemaining(TimeDelta* remaining) const {
- if (total_bytes_ <= 0)
- return false; // We never received the content_length for this download.
-
- int64 speed = CurrentSpeed();
- if (speed == 0)
- return false;
-
- *remaining =
- TimeDelta::FromSeconds((total_bytes_ - received_bytes_) / speed);
- return true;
-}
-
-int64 DownloadItem::CurrentSpeed() const {
- uintptr_t diff = GetTickCount() - start_tick_;
- return diff == 0 ? 0 : received_bytes_ * 1000 / diff;
-}
-
-int DownloadItem::PercentComplete() const {
- int percent = -1;
- if (total_bytes_ > 0)
- percent = static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
- return percent;
-}
-
-void DownloadItem::Rename(const std::wstring& full_path) {
- DCHECK(!full_path.empty());
- full_path_ = full_path;
- file_name_ = file_util::GetFilenameFromPath(full_path_);
-}
-
-void DownloadItem::TogglePause() {
- DCHECK(state_ == IN_PROGRESS);
- manager_->PauseDownload(id_, !is_paused_);
- is_paused_ = !is_paused_;
- UpdateObservers();
-}
-
-// DownloadManager implementation ----------------------------------------------
-
-// static
-void DownloadManager::RegisterUserPrefs(PrefService* prefs) {
- prefs->RegisterBooleanPref(prefs::kPromptForDownload, false);
- prefs->RegisterStringPref(prefs::kDownloadExtensionsToOpen, L"");
- prefs->RegisterBooleanPref(prefs::kDownloadDirUpgraded, false);
-
- // The default download path is userprofile\download.
- std::wstring default_download_path;
- if (!PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_download_path)) {
- NOTREACHED();
- }
- file_util::AppendToPath(&default_download_path,
- l10n_util::GetString(IDS_DOWNLOAD_DIRECTORY));
- prefs->RegisterStringPref(prefs::kDownloadDefaultDirectory,
- default_download_path);
-
- // If the download path is dangerous we forcefully reset it. But if we do
- // so we set a flag to make sure we only do it once, to avoid fighting
- // the user if he really wants it on an unsafe place such as the desktop.
-
- if (!prefs->GetBoolean(prefs::kDownloadDirUpgraded)) {
- std::wstring current_download_dir =
- prefs->GetString(prefs::kDownloadDefaultDirectory);
- if (DownloadPathIsDangerous(current_download_dir)) {
- prefs->SetString(prefs::kDownloadDefaultDirectory,
- default_download_path);
- }
- prefs->SetBoolean(prefs::kDownloadDirUpgraded, true);
- }
-}
-
-DownloadManager::DownloadManager()
- : shutdown_needed_(false),
- profile_(NULL),
- file_manager_(NULL),
- ui_loop_(MessageLoop::current()),
- file_loop_(NULL) {
-}
-
-DownloadManager::~DownloadManager() {
- if (shutdown_needed_)
- Shutdown();
-}
-
-void DownloadManager::Shutdown() {
- DCHECK(shutdown_needed_) << "Shutdown called when not needed.";
-
- // Stop receiving download updates
- file_manager_->RemoveDownloadManager(this);
-
- // Stop making history service requests
- cancelable_consumer_.CancelAllRequests();
-
- // '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();
- for (; it != in_progress_.end(); ++it) {
- DownloadItem* download = it->second;
- if (download->state() == DownloadItem::IN_PROGRESS) {
- download->Cancel(false);
- UpdateHistoryForDownload(download);
- }
- if (download->db_handle() == kUninitializedHandle) {
- // An invalid handle means that 'download' does not yet exist in
- // 'downloads_', so we have to delete it here.
- delete download;
- }
- }
-
- in_progress_.clear();
- STLDeleteValues(&downloads_);
-
- file_manager_ = NULL;
-
- // Save our file extensions to auto open.
- SaveAutoOpens();
-
- // Make sure the save as dialog doesn't notify us back if we're gone before
- // it returns.
- if (select_file_dialog_.get())
- select_file_dialog_->ListenerDestroyed();
-
- shutdown_needed_ = false;
-}
-
-// Issue a history query for downloads matching 'search_text'. If 'search_text'
-// is empty, return all downloads that we know about.
-void DownloadManager::GetDownloads(Observer* observer,
- const std::wstring& search_text) {
- DCHECK(observer);
-
- // Return a empty list if we've not yet received the set of downloads from the
- // history system (we'll update all observers once we get that list in
- // OnQueryDownloadEntriesComplete), or if there are no downloads at all.
- std::vector<DownloadItem*> download_copy;
- if (downloads_.empty()) {
- observer->SetDownloads(download_copy);
- return;
- }
-
- // We already know all the downloads and there is no filter, so just return a
- // copy to the observer.
- if (search_text.empty()) {
- download_copy.reserve(downloads_.size());
- for (DownloadMap::iterator it = downloads_.begin();
- it != downloads_.end(); ++it) {
- download_copy.push_back(it->second);
- }
-
- // We retain ownership of the DownloadItems.
- observer->SetDownloads(download_copy);
- return;
- }
-
- // Issue a request to the history service for a list of downloads matching
- // our search text.
- HistoryService* hs =
- profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- if (hs) {
- HistoryService::Handle h =
- hs->SearchDownloads(search_text,
- &cancelable_consumer_,
- NewCallback(this,
- &DownloadManager::OnSearchComplete));
- cancelable_consumer_.SetClientData(hs, h, observer);
- }
-}
-
-// 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;
- request_context_ = profile_->GetRequestContext();
-
- // 'incognito mode' will have access to past downloads, but we won't store
- // information about new downloads while in that mode.
- QueryHistoryForDownloads();
-
- ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
- if (!rdh) {
- NOTREACHED();
- return false;
- }
-
- file_manager_ = rdh->download_file_manager();
- if (!file_manager_) {
- NOTREACHED();
- return false;
- }
-
- file_loop_ = g_browser_process->file_thread()->message_loop();
- if (!file_loop_) {
- NOTREACHED();
- return false;
- }
-
- // Get our user preference state.
- PrefService* prefs = profile_->GetPrefs();
- DCHECK(prefs);
- prompt_for_download_.Init(prefs::kPromptForDownload, prefs, NULL);
-
- download_path_.Init(prefs::kDownloadDefaultDirectory, prefs, NULL);
-
- // Ensure that the download directory specified in the preferences exists.
- file_loop_->PostTask(FROM_HERE, NewRunnableMethod(
- file_manager_, &DownloadFileManager::CreateDirectory, *download_path_));
-
- // We store any file extension that should be opened automatically at
- // download completion in this pref.
- download_util::InitializeExeTypes(&exe_types_);
-
- std::wstring extensions_to_open =
- prefs->GetString(prefs::kDownloadExtensionsToOpen);
- std::vector<std::wstring> extensions;
- SplitString(extensions_to_open, L':', &extensions);
- for (size_t i = 0; i < extensions.size(); ++i) {
- if (!extensions[i].empty() && !IsExecutable(extensions[i]))
- auto_open_.insert(extensions[i]);
- }
-
- return true;
-}
-
-void DownloadManager::QueryHistoryForDownloads() {
- HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- if (hs) {
- hs->QueryDownloads(
- &cancelable_consumer_,
- NewCallback(this, &DownloadManager::OnQueryDownloadEntriesComplete));
- }
-}
-
-// We have received a message from DownloadFileManager about a new download. We
-// 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.
-void DownloadManager::StartDownload(DownloadCreateInfo* info) {
- DCHECK(MessageLoop::current() == ui_loop_);
- DCHECK(info);
-
- // Determine the proper path for a download, by choosing either the default
- // download directory, or prompting the user.
- std::wstring generated_name;
- GenerateFilename(info, &generated_name);
- if (*prompt_for_download_ && !last_download_path_.empty())
- info->suggested_path = last_download_path_;
- else
- info->suggested_path = *download_path_;
- file_util::AppendToPath(&info->suggested_path, generated_name);
-
- // We need to move over to the download thread because we don't want to stat
- // the suggested path on the UI thread.
- file_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(this,
- &DownloadManager::CheckIfSuggestedPathExists,
- info));
-}
-
-void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info) {
- DCHECK(info);
-
- // Check writability of the suggested path. If we can't write to it, default
- // to the user's "My Documents" directory. We'll prompt them in this case.
- std::wstring path = file_util::GetDirectoryFromPath(info->suggested_path);
- if (!file_util::PathIsWritable(path)) {
- info->save_as = true;
- const std::wstring filename =
- file_util::GetFilenameFromPath(info->suggested_path);
- PathService::Get(chrome::DIR_USER_DOCUMENTS, &info->suggested_path);
- file_util::AppendToPath(&info->suggested_path, filename);
- }
-
- info->suggested_path_exists = !UniquifyPath(&info->suggested_path);
-
- // Now we return to the UI thread.
- ui_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(this,
- &DownloadManager::OnPathExistenceAvailable,
- info));
-}
-
-void DownloadManager::OnPathExistenceAvailable(DownloadCreateInfo* info) {
- DCHECK(MessageLoop::current() == ui_loop_);
- DCHECK(info);
-
- if (*prompt_for_download_ || info->save_as || info->suggested_path_exists) {
- // We must ask the user for the place to put the download.
- if (!select_file_dialog_.get())
- select_file_dialog_ = SelectFileDialog::Create(this);
-
- TabContents* contents = tab_util::GetTabContentsByID(
- info->render_process_id, info->render_view_id);
- HWND owning_hwnd =
- contents ? GetAncestor(contents->GetContainerHWND(), GA_ROOT) : NULL;
- select_file_dialog_->SelectFile(SelectFileDialog::SELECT_SAVEAS_FILE,
- std::wstring(), info->suggested_path,
- owning_hwnd, info);
- } else {
- // No prompting for download, just continue with the suggested name.
- ContinueStartDownload(info, info->suggested_path);
- }
-}
-
-void DownloadManager::ContinueStartDownload(DownloadCreateInfo* info,
- const std::wstring& target_path) {
- scoped_ptr<DownloadCreateInfo> infop(info);
- info->path = target_path;
-
- DownloadItem* download = NULL;
- DownloadMap::iterator it = in_progress_.find(info->download_id);
- if (it == in_progress_.end()) {
- download = new DownloadItem(info->download_id,
- info->path,
- info->url,
- info->start_time,
- info->total_bytes,
- info->render_process_id,
- info->request_id);
- download->set_manager(this);
- in_progress_[info->download_id] = download;
- } else {
- NOTREACHED(); // Should not exist!
- return;
- }
-
- // If the download already completed by the time we reached this point, then
- // notify observers that it did.
- PendingFinishedMap::iterator pending_it =
- pending_finished_downloads_.find(info->download_id);
- if (pending_it != pending_finished_downloads_.end())
- DownloadFinished(pending_it->first, pending_it->second);
-
- download->Rename(target_path);
-
- file_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(file_manager_,
- &DownloadFileManager::OnFinalDownloadName,
- download->id(),
- target_path));
-
- if (profile_->IsOffTheRecord()) {
- // Fake a db handle for incognito mode, since nothing is actually stored in
- // the database in this mode. We have to make sure that these handles don't
- // collide with normal db handles, so we use a negative value. Eventually,
- // they could overlap, but you'd have to do enough downloading that your ISP
- // would likely stab you in the neck first. YMMV.
- static int64 fake_db_handle = kUninitializedHandle - 1;
- OnCreateDownloadEntryComplete(*info, fake_db_handle--);
- } else {
- // Update the history system with the new download.
- // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
- HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- if (hs) {
- hs->CreateDownload(
- *info, &cancelable_consumer_,
- NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete));
- }
- }
-}
-
-// Convenience function for updating the history service for a download.
-void DownloadManager::UpdateHistoryForDownload(DownloadItem* download) {
- DCHECK(download);
-
- // Don't store info in the database if the download was initiated while in
- // incognito mode or if it hasn't been initialized in our database table.
- if (download->db_handle() <= kUninitializedHandle)
- return;
-
- // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
- HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- if (hs) {
- hs->UpdateDownload(download->received_bytes(),
- download->state(),
- download->db_handle());
- }
-}
-
-void DownloadManager::RemoveDownloadFromHistory(DownloadItem* download) {
- DCHECK(download);
- // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
- HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- if (download->db_handle() > kUninitializedHandle && hs)
- hs->RemoveDownload(download->db_handle());
-}
-
-void DownloadManager::RemoveDownloadsFromHistoryBetween(const Time remove_begin,
- const Time remove_end) {
- // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
- HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- if (hs)
- hs->RemoveDownloadsBetween(remove_begin, remove_end);
-}
-
-void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
- DownloadMap::iterator it = in_progress_.find(download_id);
- if (it != in_progress_.end()) {
- DownloadItem* download = it->second;
- download->Update(size);
- UpdateHistoryForDownload(download);
- }
-}
-
-void DownloadManager::DownloadFinished(int32 download_id, int64 size) {
- DownloadMap::iterator it = in_progress_.find(download_id);
- if (it != in_progress_.end()) {
- // Remove the id from the list of pending ids.
- PendingFinishedMap::iterator erase_it =
- pending_finished_downloads_.find(download_id);
- if (erase_it != pending_finished_downloads_.end())
- pending_finished_downloads_.erase(erase_it);
-
- DownloadItem* download = it->second;
- download->Finished(size);
-
- // Open the download if the user or user prefs indicate it should be.
- const std::wstring extension =
- file_util::GetFileExtensionFromPath(download->full_path());
- if (download->open_when_complete() || ShouldOpenFileExtension(extension))
- OpenDownloadInShell(download, NULL);
-
- // 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() != kUninitializedHandle) {
- in_progress_.erase(it);
- NotifyAboutDownloadStop();
- UpdateHistoryForDownload(download);
- }
- } else {
- // The download is done, but the user hasn't selected a final location for
- // it yet (the Save As dialog box is probably still showing), so just keep
- // track of the fact that this download id is complete, when the
- // DownloadItem is constructed later we'll notify its completion then.
- PendingFinishedMap::iterator erase_it =
- pending_finished_downloads_.find(download_id);
- DCHECK(erase_it == pending_finished_downloads_.end());
- pending_finished_downloads_[download_id] = size;
- }
-}
-
-// static
-// We have to tell the ResourceDispatcherHost to cancel the download from this
-// thread, since we can't forward tasks from the file thread to the io thread
-// reliably (crash on shutdown race condition).
-void DownloadManager::CancelDownloadRequest(int render_process_id,
- int request_id) {
- ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
- base::Thread* io_thread = g_browser_process->io_thread();
- if (!io_thread || !rdh)
- return;
- io_thread->message_loop()->PostTask(FROM_HERE,
- NewRunnableFunction(&DownloadManager::OnCancelDownloadRequest,
- rdh,
- render_process_id,
- request_id));
-}
-
-// static
-void DownloadManager::OnCancelDownloadRequest(ResourceDispatcherHost* rdh,
- int render_process_id,
- int request_id) {
- rdh->CancelRequest(render_process_id, request_id, false);
-}
-
-void DownloadManager::DownloadCancelled(int32 download_id) {
- DownloadMap::iterator it = in_progress_.find(download_id);
- if (it == in_progress_.end())
- return;
- DownloadItem* download = it->second;
-
- CancelDownloadRequest(download->render_process_id(), download->request_id());
-
- // 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() != kUninitializedHandle) {
- in_progress_.erase(it);
- NotifyAboutDownloadStop();
- UpdateHistoryForDownload(download);
- }
-
- // Tell the file manager to cancel the download.
- file_manager_->RemoveDownload(download->id(), this); // On the UI thread
- file_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(file_manager_,
- &DownloadFileManager::CancelDownload,
- download->id()));
-}
-
-void DownloadManager::PauseDownload(int32 download_id, bool pause) {
- DownloadMap::iterator it = in_progress_.find(download_id);
- if (it != in_progress_.end()) {
- DownloadItem* download = it->second;
- if (pause == download->is_paused())
- return;
-
- // Inform the ResourceDispatcherHost of the new pause state.
- base::Thread* io_thread = g_browser_process->io_thread();
- ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
- if (!io_thread || !rdh)
- return;
-
- io_thread->message_loop()->PostTask(FROM_HERE,
- NewRunnableFunction(&DownloadManager::OnPauseDownloadRequest,
- rdh,
- download->render_process_id(),
- download->request_id(),
- pause));
- }
-}
-
-// static
-void DownloadManager::OnPauseDownloadRequest(ResourceDispatcherHost* rdh,
- int render_process_id,
- int request_id,
- bool pause) {
- rdh->PauseRequest(render_process_id, request_id, pause);
-}
-
-void DownloadManager::RemoveDownload(int64 download_handle) {
- DownloadMap::iterator it = downloads_.find(download_handle);
- if (it == downloads_.end())
- return;
-
- // Make history update.
- DownloadItem* download = it->second;
- RemoveDownloadFromHistory(download);
-
- // Remove from our tables and delete.
- downloads_.erase(it);
- delete download;
-
- // Tell observers to refresh their views.
- FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
-}
-
-int DownloadManager::RemoveDownloadsBetween(const Time remove_begin,
- const Time remove_end) {
- RemoveDownloadsFromHistoryBetween(remove_begin, remove_end);
-
- int num_deleted = 0;
- DownloadMap::iterator it = downloads_.begin();
- while (it != downloads_.end()) {
- DownloadItem* download = it->second;
- DownloadItem::DownloadState state = download->state();
- if (download->start_time() >= remove_begin &&
- (remove_end.is_null() || download->start_time() < remove_end) &&
- (state == DownloadItem::COMPLETE ||
- state == DownloadItem::CANCELLED)) {
- // Remove from the map and move to the next in the list.
- it = downloads_.erase(it);
- delete download;
-
- ++num_deleted;
- continue;
- }
-
- ++it;
- }
-
- // Tell observers to refresh their views.
- if (num_deleted > 0)
- FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
-
- return num_deleted;
-}
-
-int DownloadManager::RemoveDownloads(const Time remove_begin) {
- return RemoveDownloadsBetween(remove_begin, 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,
- WebContents* web_contents) {
- DCHECK(web_contents);
- file_manager_->DownloadUrl(url,
- referrer,
- web_contents->process()->host_id(),
- web_contents->render_view_host()->routing_id(),
- request_context_.get());
-}
-
-void DownloadManager::NotifyAboutDownloadStart() {
- NotificationService::current()->
- Notify(NOTIFY_DOWNLOAD_START, NotificationService::AllSources(),
- NotificationService::NoDetails());
-}
-
-void DownloadManager::NotifyAboutDownloadStop() {
- NotificationService::current()->
- Notify(NOTIFY_DOWNLOAD_STOP, NotificationService::AllSources(),
- NotificationService::NoDetails());
-}
-
-void DownloadManager::GenerateExtension(const std::wstring& file_name,
- const std::string& mime_type,
- std::wstring* generated_extension) {
- // We're worried about three things here:
- //
- // 1) Security. Many sites let users upload content, such as buddy icons, to
- // their web sites. We want to mitigate the case where an attacker
- // supplies a malicious executable with an executable file extension but an
- // honest site serves the content with a benign content type, such as
- // image/jpeg.
- //
- // 2) Usability. If the site fails to provide a file extension, we want to
- // guess a reasonable file extension based on the content type.
- //
- // 3) Shell integration. Some file extensions automatically integrate with
- // the shell. We block these extensions to prevent a malicious web site
- // from integrating with the user's shell.
-
- static const wchar_t default_extension[] = L"download";
-
- // See if our file name already contains an extension.
- std::wstring extension(file_util::GetFileExtensionFromPath(file_name));
-
- // Rename shell-integrated extensions.
- if (win_util::IsShellIntegratedExtension(extension))
- extension.assign(default_extension);
-
- std::string mime_type_from_extension;
- net::GetMimeTypeFromFile(file_name, &mime_type_from_extension);
- if (mime_type == mime_type_from_extension) {
- // The hinted extension matches the mime type. It looks like a winner.
- generated_extension->swap(extension);
- return;
- }
-
- if (IsExecutable(extension) && !IsExecutableMimeType(mime_type)) {
- // We want to be careful about executable extensions. The worry here is
- // that a trusted web site could be tricked into dropping an executable file
- // on the user's filesystem.
- if (!net::GetPreferredExtensionForMimeType(mime_type, &extension)) {
- // We couldn't find a good extension for this content type. Use a dummy
- // extension instead.
- extension.assign(default_extension);
- }
- }
-
- if (extension.empty()) {
- net::GetPreferredExtensionForMimeType(mime_type, &extension);
- } else {
- // Append entension generated from the mime type if:
- // 1. New extension is not ".txt"
- // 2. New extension is not the same as the already existing extension.
- // 3. New extension is not executable. This action mitigates the case when
- // an execuatable is hidden in a benign file extension;
- // E.g. my-cat.jpg becomes my-cat.jpg.js if content type is
- // application/x-javascript.
- std::wstring append_extension;
- if (net::GetPreferredExtensionForMimeType(mime_type, &append_extension)) {
- if (append_extension != L".txt" && append_extension != extension &&
- !IsExecutable(append_extension))
- extension += append_extension;
- }
- }
-
- generated_extension->swap(extension);
-}
-
-void DownloadManager::GenerateFilename(DownloadCreateInfo* info,
- std::wstring* generated_name) {
- std::wstring file_name =
- net::GetSuggestedFilename(GURL(info->url),
- info->content_disposition,
- L"download");
- DCHECK(!file_name.empty());
-
- // Make sure we get the right file extension.
- std::wstring extension;
- GenerateExtension(file_name, info->mime_type, &extension);
- file_util::ReplaceExtension(&file_name, extension);
-
- // Prepend "_" to the file name if it's a reserved name
- if (win_util::IsReservedName(file_name))
- file_name = std::wstring(L"_") + file_name;
-
- generated_name->assign(file_name);
-}
-
-void DownloadManager::AddObserver(Observer* observer) {
- observers_.AddObserver(observer);
- observer->ModelChanged();
-}
-
-void DownloadManager::RemoveObserver(Observer* observer) {
- observers_.RemoveObserver(observer);
-}
-
-// Post Windows Shell operations to the Download thread, to avoid blocking the
-// user interface.
-void DownloadManager::ShowDownloadInShell(const DownloadItem* download) {
- DCHECK(file_manager_);
- file_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(file_manager_,
- &DownloadFileManager::OnShowDownloadInShell,
- download->full_path()));
-}
-
-void DownloadManager::OpenDownloadInShell(const DownloadItem* download,
- HWND parent_window) {
- DCHECK(file_manager_);
- file_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(file_manager_,
- &DownloadFileManager::OnOpenDownloadInShell,
- download->full_path(), download->url(), parent_window));
-}
-
-void DownloadManager::OpenFilesOfExtension(const std::wstring& extension,
- bool open) {
- if (open && !IsExecutable(extension))
- auto_open_.insert(extension);
- else
- auto_open_.erase(extension);
- SaveAutoOpens();
-}
-
-bool DownloadManager::ShouldOpenFileExtension(const std::wstring& extension) {
- if (!IsExecutable(extension) &&
- auto_open_.find(extension) != auto_open_.end())
- return true;
- return false;
-}
-
-// static
-bool DownloadManager::IsExecutableMimeType(const std::string& mime_type) {
- // JavaScript is just as powerful as EXE.
- if (net::MatchesMimeType("text/javascript", mime_type))
- return true;
- if (net::MatchesMimeType("text/javascript;version=*", mime_type))
- return true;
-
- // We don't consider other non-application types to be executable.
- if (!net::MatchesMimeType("application/*", mime_type))
- return false;
-
- // These application types are not executable.
- if (net::MatchesMimeType("application/*+xml", mime_type))
- return false;
- if (net::MatchesMimeType("application/xml", mime_type))
- return false;
-
- return true;
-}
-
-bool DownloadManager::IsExecutable(const std::wstring& extension) {
- return exe_types_.find(extension) != exe_types_.end();
-}
-
-void DownloadManager::ResetAutoOpenFiles() {
- auto_open_.clear();
- SaveAutoOpens();
-}
-
-bool DownloadManager::HasAutoOpenFileTypesRegistered() const {
- return !auto_open_.empty();
-}
-
-void DownloadManager::SaveAutoOpens() {
- PrefService* prefs = profile_->GetPrefs();
- if (prefs) {
- std::wstring extensions;
- for (std::set<std::wstring>::iterator it = auto_open_.begin();
- it != auto_open_.end(); ++it) {
- extensions += *it + L":";
- }
- if (!extensions.empty())
- extensions.erase(extensions.size() - 1);
- prefs->SetString(prefs::kDownloadExtensionsToOpen, extensions);
- }
-}
-
-void DownloadManager::FileSelected(const std::wstring& path, void* params) {
- DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params);
- if (*prompt_for_download_)
- last_download_path_ = file_util::GetDirectoryFromPath(path);
- ContinueStartDownload(info, 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.
- DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params);
- file_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(file_manager_, &DownloadFileManager::CancelDownload,
- info->download_id));
-}
-
-// Operations posted to us from the history service ----------------------------
-
-// The history service has retrieved all download entries. 'entries' contains
-// 'DownloadCreateInfo's in sorted order (by ascending start_time).
-void DownloadManager::OnQueryDownloadEntriesComplete(
- std::vector<DownloadCreateInfo>* entries) {
- for (size_t i = 0; i < entries->size(); ++i) {
- DownloadItem* download = new DownloadItem(entries->at(i));
- DCHECK(downloads_.find(download->db_handle()) == downloads_.end());
- downloads_[download->db_handle()] = download;
- download->set_manager(this);
- }
- FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
-}
-
-
-// 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.
-void DownloadManager::OnCreateDownloadEntryComplete(DownloadCreateInfo info,
- int64 db_handle) {
- DownloadMap::iterator it = in_progress_.find(info.download_id);
- DCHECK(it != in_progress_.end());
-
- DownloadItem* download = it->second;
- DCHECK(download->db_handle() == kUninitializedHandle);
- download->set_db_handle(db_handle);
-
- // Insert into our full map.
- DCHECK(downloads_.find(download->db_handle()) == downloads_.end());
- downloads_[download->db_handle()] = download;
-
- // The 'contents' may no longer exist if the user closed the tab before we get
- // this start completion event. If it does, tell the origin WebContents to
- // display its download shelf.
- TabContents* contents =
- tab_util::GetTabContentsByID(info.render_process_id, info.render_view_id);
-
- // If the contents no longer exists or is no longer active, we start the
- // download in the last active browser. This is not ideal but better than
- // fully hiding the download from the user. Note: non active means that the
- // user navigated away from the tab contents. This has nothing to do with
- // tab selection.
- if (!contents || !contents->is_active()) {
- Browser* last_active = BrowserList::GetLastActive();
- if (last_active)
- contents = last_active->GetSelectedTabContents();
- }
-
- if (contents)
- contents->OnStartDownload(download);
-
- // Inform interested objects about the new download.
- FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
- NotifyAboutDownloadStart();
-
- // If this download has been completed before we've received the db handle,
- // 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->state() != DownloadItem::IN_PROGRESS) {
- in_progress_.erase(it);
- NotifyAboutDownloadStop();
- UpdateHistoryForDownload(download);
- download->UpdateObservers();
- }
-}
-
-// Called when the history service has retrieved the list of downloads that
-// match the search text.
-void DownloadManager::OnSearchComplete(HistoryService::Handle handle,
- std::vector<int64>* results) {
- HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- Observer* requestor = cancelable_consumer_.GetClientData(hs, handle);
- if (!requestor)
- return;
-
- std::vector<DownloadItem*> searched_downloads;
- for (std::vector<int64>::iterator it = results->begin();
- it != results->end(); ++it) {
- DownloadMap::iterator dit = downloads_.find(*it);
- if (dit != downloads_.end())
- searched_downloads.push_back(dit->second);
- }
-
- requestor->SetDownloads(searched_downloads);
-}
-
« no previous file with comments | « chrome/browser/download_manager.h ('k') | chrome/browser/download_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698