blob: 50f4fa37b7582644cb62c1cb8edcbb986ab91014 [file] [log] [blame]
// Copyright (c) 2006-2009 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.
//
// The DownloadManager object manages the process of downloading, including
// updates to the history system and providing the information for displaying
// the downloads view in the Destinations tab. There is one DownloadManager per
// active profile in Chrome.
//
// Each download is represented by a DownloadItem, and all DownloadItems
// are owned by the DownloadManager which maintains a global list of all
// downloads. DownloadItems are created when a user initiates a download,
// and exist for the duration of the browser life time.
//
// Download observers:
// Objects that are interested in notifications about new downloads, or progress
// updates for a given download must implement one of the download observer
// interfaces:
// DownloadItem::Observer:
// - allows observers to receive notifications about one download from start
// to completion
// DownloadManager::Observer:
// - allows observers, primarily views, to be notified when changes to the
// set of all downloads (such as new downloads, or deletes) occur
// Use AddObserver() / RemoveObserver() on the appropriate download object to
// receive state updates.
//
// Download state persistence:
// The DownloadManager uses the history service for storing persistent
// information about the state of all downloads. The history system maintains a
// separate table for this called 'downloads'. At the point that the
// DownloadManager is constructed, we query the history service for the state of
// all persisted downloads.
#ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_H_
#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_H_
#include "build/build_config.h"
#include <string>
#include <map>
#include <set>
#include <vector>
#include "base/basictypes.h"
#include "base/file_path.h"
#include "base/hash_tables.h"
#include "base/observer_list.h"
#include "base/ref_counted.h"
#include "base/time.h"
#include "chrome/browser/cancelable_request.h"
#include "chrome/browser/history/download_types.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/shell_dialogs.h"
#include "chrome/common/pref_member.h"
class DownloadFileManager;
class DownloadItemView;
class DownloadManager;
class GURL;
class MessageLoop;
class PrefService;
class Profile;
class ResourceDispatcherHost;
class URLRequestContext;
class WebContents;
namespace base {
class Thread;
}
// DownloadItem ----------------------------------------------------------------
// One DownloadItem per download. This is the model class that stores all the
// state for a download. Multiple views, such as a tab's download shelf and the
// Destination tab's download view, may refer to a given DownloadItem.
class DownloadItem {
public:
enum DownloadState {
IN_PROGRESS,
COMPLETE,
CANCELLED,
REMOVING
};
enum SafetyState {
SAFE = 0,
DANGEROUS,
DANGEROUS_BUT_VALIDATED // Dangerous but the user confirmed the download.
};
// Interface that observers of a particular download must implement in order
// to receive updates to the download's status.
class Observer {
public:
virtual void OnDownloadUpdated(DownloadItem* download) = 0;
// Called when a downloaded file has been opened.
virtual void OnDownloadOpened(DownloadItem* download) = 0;
};
// Constructing from persistent store:
DownloadItem(const DownloadCreateInfo& info);
// Constructing from user action:
DownloadItem(int32 download_id,
const FilePath& path,
int path_uniquifier,
const GURL& url,
const FilePath& original_name,
const base::Time start_time,
int64 download_size,
int render_process_id,
int request_id,
bool is_dangerous);
~DownloadItem();
void Init(bool start_timer);
// Public API
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Notifies our observers periodically.
void UpdateObservers();
// Notifies our observers the downloaded file has been opened.
void NotifyObserversDownloadOpened();
// Received a new chunk of data
void Update(int64 bytes_so_far);
// Cancel the download operation. We need to distinguish between cancels at
// exit (DownloadManager destructor) from user interface initiated cancels
// because at exit, the history system may not exist, and any updates to it
// require AddRef'ing the DownloadManager in the destructor which results in
// a DCHECK failure. Set 'update_history' to false when canceling from at
// exit to prevent this crash. This may result in a difference between the
// downloaded file's size on disk, and what the history system's last record
// of it is. At worst, we'll end up re-downloading a small portion of the file
// when resuming a download (assuming the server supports byte ranges).
void Cancel(bool update_history);
// Download operation completed.
void Finished(int64 size);
// The user wants to remove the download from the views and history. If
// |delete_file| is true, the file is deleted on the disk.
void Remove(bool delete_file);
// Start/stop sending periodic updates to our observers
void StartProgressTimer();
void StopProgressTimer();
// Simple calculation of the amount of time remaining to completion. Fills
// |*remaining| with the amount of time remaining if successful. Fails and
// returns false if we do not have the number of bytes or the speed so can
// not estimate.
bool TimeRemaining(base::TimeDelta* remaining) const;
// Simple speed estimate in bytes/s
int64 CurrentSpeed() const;
// Rough percent complete, -1 means we don't know (since we didn't receive a
// total size).
int PercentComplete() const;
// Update the download's path, the actual file is renamed on the download
// thread.
void Rename(const FilePath& full_path);
// Allow the user to temporarily pause a download or resume a paused download.
void TogglePause();
// Accessors
DownloadState state() const { return state_; }
FilePath file_name() const { return file_name_; }
void set_file_name(const FilePath& name) { file_name_ = name; }
FilePath full_path() const { return full_path_; }
void set_full_path(const FilePath& path) { full_path_ = path; }
int path_uniquifier() const { return path_uniquifier_; }
void set_path_uniquifier(int uniquifier) { path_uniquifier_ = uniquifier; }
GURL url() const { return url_; }
int64 total_bytes() const { return total_bytes_; }
void set_total_bytes(int64 total_bytes) { total_bytes_ = total_bytes; }
int64 received_bytes() const { return received_bytes_; }
int32 id() const { return id_; }
base::Time start_time() const { return start_time_; }
void set_db_handle(int64 handle) { db_handle_ = handle; }
int64 db_handle() const { return db_handle_; }
DownloadManager* manager() const { return manager_; }
void set_manager(DownloadManager* manager) { manager_ = manager; }
bool is_paused() const { return is_paused_; }
void set_is_paused(bool pause) { is_paused_ = pause; }
bool open_when_complete() const { return open_when_complete_; }
void set_open_when_complete(bool open) { open_when_complete_ = open; }
int render_process_id() const { return render_process_id_; }
int request_id() const { return request_id_; }
SafetyState safety_state() const { return safety_state_; }
void set_safety_state(SafetyState safety_state) {
safety_state_ = safety_state;
}
FilePath original_name() const { return original_name_; }
void set_original_name(const FilePath& name) { original_name_ = name; }
// Returns the file-name that should be reported to the user, which is
// file_name_ for safe downloads and original_name_ for dangerous ones with
// the uniquifier number.
FilePath GetFileName() const;
private:
// Internal helper for maintaining consistent received and total sizes.
void UpdateSize(int64 size);
// Request ID assigned by the ResourceDispatcherHost.
int32 id_;
// Full path to the downloaded file
FilePath full_path_;
// A number that should be appended to the path to make it unique, or 0 if the
// path should be used as is.
int path_uniquifier_;
// Short display version of the file
FilePath file_name_;
// The URL from whence we came.
GURL url_;
// Total bytes expected
int64 total_bytes_;
// Current received bytes
int64 received_bytes_;
// Start time for calculating remaining time
base::TimeTicks start_tick_;
// The current state of this download
DownloadState state_;
// The views of this item in the download shelf and download tab
ObserverList<Observer> observers_;
// Time the download was started
base::Time start_time_;
// Our persistent store handle
int64 db_handle_;
// Timer for regularly updating our observers
base::RepeatingTimer<DownloadItem> update_timer_;
// Our owning object
DownloadManager* manager_;
// In progress downloads may be paused by the user, we note it here
bool is_paused_;
// A flag for indicating if the download should be opened at completion.
bool open_when_complete_;
// Whether the download is considered potentially safe or dangerous
// (executable files are typically considered dangerous).
SafetyState safety_state_;
// Dangerous download are given temporary names until the user approves them.
// This stores their original name.
FilePath original_name_;
// For canceling or pausing requests.
int render_process_id_;
int request_id_;
DISALLOW_EVIL_CONSTRUCTORS(DownloadItem);
};
// DownloadManager -------------------------------------------------------------
// Browser's download manager: manages all downloads and destination view.
class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>,
public SelectFileDialog::Listener {
// For testing.
friend class DownloadManagerTest;
public:
DownloadManager();
~DownloadManager();
static void RegisterUserPrefs(PrefService* prefs);
// Interface to implement for observers that wish to be informed of changes
// to the DownloadManager's collection of downloads.
class Observer {
public:
// New or deleted download, observers should query us for the current set
// of downloads.
virtual void ModelChanged() = 0;
// A callback once the DownloadManager has retrieved the requested set of
// downloads. The DownloadManagerObserver must copy the vector, but does not
// own the individual DownloadItems, when this call is made.
virtual void SetDownloads(std::vector<DownloadItem*>& downloads) = 0;
};
// Public API
// Begin a search for all downloads matching 'search_text'. If 'search_text'
// is empty, return all known downloads. The results are returned in the
// 'SetDownloads' observer callback.
void GetDownloads(Observer* observer,
const std::wstring& search_text);
// Returns true if initialized properly.
bool Init(Profile* profile);
// Schedule a query of the history service to retrieve all downloads.
void QueryHistoryForDownloads();
// Notifications sent from the download thread to the UI thread
void StartDownload(DownloadCreateInfo* info);
void UpdateDownload(int32 download_id, int64 size);
void DownloadFinished(int32 download_id, int64 size);
// Helper method for cancelling the network request associated with a
// download.
static void CancelDownloadRequest(int render_process_id, int request_id);
// Called from a view when a user clicks a UI button or link.
void DownloadCancelled(int32 download_id);
void PauseDownload(int32 download_id, bool pause);
void RemoveDownload(int64 download_handle);
// Called when the download is renamed to its final name.
void DownloadRenamedToFinalName(int download_id, const FilePath& full_path);
// Remove downloads after remove_begin (inclusive) and before remove_end
// (exclusive). You may pass in null Time values to do an unbounded delete
// in either direction.
int RemoveDownloadsBetween(const base::Time remove_begin,
const base::Time remove_end);
// Remove downloads will delete all downloads that have a timestamp that is
// the same or more recent than |remove_begin|. The number of downloads
// deleted is returned back to the caller.
int RemoveDownloads(const base::Time remove_begin);
// Remove all downloads will delete all downloads. The number of downloads
// deleted is returned back to the caller.
int RemoveAllDownloads();
// Download the object at the URL. Used in cases such as "Save Link As..."
void DownloadUrl(const GURL& url,
const GURL& referrer,
const std::string& referrer_encoding,
WebContents* web_contents);
// Allow objects to observe the download creation process.
void AddObserver(Observer* observer);
// Remove a download observer from ourself.
void RemoveObserver(Observer* observer);
// Methods called on completion of a query sent to the history system.
void OnQueryDownloadEntriesComplete(
std::vector<DownloadCreateInfo>* entries);
void OnCreateDownloadEntryComplete(DownloadCreateInfo info, int64 db_handle);
void OnSearchComplete(HistoryService::Handle handle,
std::vector<int64>* results);
// Opens a download. For Chrome extensions call
// ExtensionsServices::InstallExtension, for everything else call
// OpenDownloadInShell.
void OpenDownload(const DownloadItem* download,
gfx::NativeView parent_window);
// Show a download via the Windows shell.
void ShowDownloadInShell(const DownloadItem* download);
// The number of in progress (including paused) downloads.
int in_progress_count() const {
return static_cast<int>(in_progress_.size());
}
FilePath download_path() {
return FilePath::FromWStringHack(*download_path_);
}
// Clears the last download path, used to initialize "save as" dialogs.
void ClearLastDownloadPath();
// Registers this file extension for automatic opening upon download
// completion if 'open' is true, or prevents the extension from automatic
// opening if 'open' is false.
void OpenFilesOfExtension(const FilePath::StringType& extension, bool open);
// Tests if a file type should be opened automatically.
bool ShouldOpenFileExtension(const FilePath::StringType& extension);
// Tests if we think the server means for this mime_type to be executable.
static bool IsExecutableMimeType(const std::string& mime_type);
// Tests if a file type is considered executable.
bool IsExecutable(const FilePath::StringType& extension);
// Resets the automatic open preference.
void ResetAutoOpenFiles();
// Returns true if there are automatic handlers registered for any file
// types.
bool HasAutoOpenFileTypesRegistered() const;
// Overridden from SelectFileDialog::Listener:
virtual void FileSelected(const FilePath& path, int index, void* params);
virtual void FileSelectionCanceled(void* params);
// Deletes the specified path on the file thread.
void DeleteDownload(const FilePath& path);
// Called when the user has validated the donwload of a dangerous file.
void DangerousDownloadValidated(DownloadItem* download);
// Used to make sure we have a safe file extension and filename for a
// download. |file_name| can either be just the file name or it can be a
// full path to a file.
void GenerateSafeFilename(const std::string& mime_type,
FilePath* file_name);
private:
// Opens a download via the Windows shell.
void OpenDownloadInShell(const DownloadItem* download,
gfx::NativeView parent_window);
// Opens downloaded Chrome extension file (*.crx).
void OpenChromeExtension(const FilePath& full_path);
// Shutdown the download manager. This call is needed only after Init.
void Shutdown();
// Called on the download thread to check whether the suggested file path
// exists. We don't check if the file exists on the UI thread to avoid UI
// stalls from interacting with the file system.
void CheckIfSuggestedPathExists(DownloadCreateInfo* info);
// Called on the UI thread once the DownloadManager has determined whether the
// suggested file path exists.
void OnPathExistenceAvailable(DownloadCreateInfo* info);
// Called back after a target path for the file to be downloaded to has been
// determined, either automatically based on the suggested file name, or by
// the user in a Save As dialog box.
void ContinueStartDownload(DownloadCreateInfo* info,
const FilePath& target_path);
// Update the history service for a particular download.
void UpdateHistoryForDownload(DownloadItem* download);
void RemoveDownloadFromHistory(DownloadItem* download);
void RemoveDownloadsFromHistoryBetween(const base::Time remove_begin,
const base::Time remove_before);
// Create an extension based on the file name and mime type.
void GenerateExtension(const FilePath& file_name,
const std::string& mime_type,
FilePath::StringType* generated_extension);
// Create a file name based on the response from the server.
void GenerateFilename(DownloadCreateInfo* info, FilePath* generated_name);
// Persist the automatic opening preference.
void SaveAutoOpens();
// Runs the network cancel on the IO thread.
static void OnCancelDownloadRequest(ResourceDispatcherHost* rdh,
int render_process_id,
int request_id);
// Runs the pause on the IO thread.
static void OnPauseDownloadRequest(ResourceDispatcherHost* rdh,
int render_process_id,
int request_id,
bool pause);
// Performs the last steps required when a download has been completed.
// It is necessary to break down the flow when a download is finished as
// dangerous downloads are downloaded to temporary files that need to be
// renamed on the file thread first.
// Invoked on the UI thread.
void ContinueDownloadFinished(DownloadItem* download);
// Renames a finished dangerous download from its temporary file name to its
// real file name.
// Invoked on the file thread.
void ProceedWithFinishedDangerousDownload(int64 download_handle,
const FilePath& path,
const FilePath& original_name);
// Invoked on the UI thread when a dangerous downloaded file has been renamed.
void DangerousDownloadRenamed(int64 download_handle,
bool success,
const FilePath& new_path,
int new_path_uniquifier);
// Checks whether a file represents a risk if downloaded.
bool IsDangerous(const FilePath& file_name);
// Changes the paths and file name of the specified |download|, propagating
// the change to the history system.
void RenameDownload(DownloadItem* download, const FilePath& new_path);
// 'downloads_' is map of all downloads in this profile. The key is the handle
// returned by the history system, which is unique across sessions. This map
// owns all the DownloadItems once they have been created in the history
// system.
//
// 'in_progress_' is a map of all downloads that are in progress and that have
// not yet received a valid history handle. The key is the ID assigned by the
// ResourceDispatcherHost, which is unique for the current session. This map
// does not own the DownloadItems.
//
// 'dangerous_finished_' is a map of dangerous download that have finished
// but were not yet approved by the user. Similarly to in_progress_, the key
// is the ID assigned by the ResourceDispatcherHost and the map does not own
// the DownloadItems. It is used on shutdown to delete completed downloads
// that have not been approved.
//
// When a download is created through a user action, the corresponding
// DownloadItem* is placed in 'in_progress_' and remains there until it has
// received a valid handle from the history system. Once it has a valid
// handle, the DownloadItem* is placed in the 'downloads_' map. When the
// download is complete, it is removed from 'in_progress_'. Downloads from
// past sessions read from a persisted state from the history system are
// placed directly into 'downloads_' since they have valid handles in the
// history system.
typedef base::hash_map<int64, DownloadItem*> DownloadMap;
DownloadMap downloads_;
DownloadMap in_progress_;
DownloadMap dangerous_finished_;
// True if the download manager has been initialized and requires a shutdown.
bool shutdown_needed_;
// Observers that want to be notified of changes to the set of downloads.
ObserverList<Observer> observers_;
// The current active profile.
Profile* profile_;
scoped_refptr<URLRequestContext> request_context_;
// Used for history service request management.
CancelableRequestConsumerTSimple<Observer*> cancelable_consumer_;
// Non-owning pointer for handling file writing on the download_thread_.
DownloadFileManager* file_manager_;
// A pointer to the main UI loop.
MessageLoop* ui_loop_;
// A pointer to the file thread's loop. The file thread lives longer than
// the DownloadManager, so this is safe to cache.
MessageLoop* file_loop_;
// User preferences
BooleanPrefMember prompt_for_download_;
StringPrefMember download_path_;
// The user's last choice for download directory. This is only used when the
// user wants us to prompt for a save location for each download.
FilePath last_download_path_;
// Set of file extensions to open at download completion.
std::set<FilePath::StringType> auto_open_;
// Set of file extensions that are executables and shouldn't be auto opened.
std::set<std::string> exe_types_;
// Keep track of downloads that are completed before the user selects the
// destination, so that observers are appropriately notified of completion
// after this determination is made.
// The map is of download_id->remaining size (bytes), both of which are
// required when calling DownloadFinished.
typedef std::map<int32, int64> PendingFinishedMap;
PendingFinishedMap pending_finished_downloads_;
// The "Save As" dialog box used to ask the user where a file should be
// saved.
scoped_refptr<SelectFileDialog> select_file_dialog_;
DISALLOW_COPY_AND_ASSIGN(DownloadManager);
};
#endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_H_