summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-10 18:50:32 +0000
committerjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-10 18:50:32 +0000
commit9ccbb370aa45f477941e0599d4ce7c89fac64101 (patch)
tree8b21818fa95d05ff00dfe5b9986d1d39eb4be722 /chrome/browser
parenta9acde54584ff37bcc5fad73cf25dce5d85348bc (diff)
downloadchromium_src-9ccbb370aa45f477941e0599d4ce7c89fac64101.zip
chromium_src-9ccbb370aa45f477941e0599d4ce7c89fac64101.tar.gz
chromium_src-9ccbb370aa45f477941e0599d4ce7c89fac64101.tar.bz2
This CL adds prompting for dangerous types of files (executable) when they are automatically downloaded.
The file is saved with a temporary name (dangerous_download_xxxx.download) in the download directory and the user is presented (in the download shelf and the download tab if opened) with a warning message and buttons to save/discard the download. If discarded the download is removed (and its file deleted). If saved, download goes as usual. Dangerous downloads not confirmed by the user are deleted on shutdown. TEST=Download a small exe file, try using the save/discard button from the download shelf and from the download tab (the intent is that the file has been entirely downloaded by the time you take action). Try again with a slow/big download (that time the download is expected not to be finished when approved/discarded). Review URL: http://codereview.chromium.org/6043 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3228 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/download/download_exe.cc3
-rw-r--r--chrome/browser/download/download_file.cc6
-rw-r--r--chrome/browser/download/download_file.h3
-rw-r--r--chrome/browser/download/download_manager.cc250
-rw-r--r--chrome/browser/download/download_manager.h79
-rw-r--r--chrome/browser/download/download_util.cc2
-rw-r--r--chrome/browser/download/save_package.cc6
-rw-r--r--chrome/browser/history/download_database.cc14
-rw-r--r--chrome/browser/history/download_database.h3
-rw-r--r--chrome/browser/history/download_types.h7
-rw-r--r--chrome/browser/history/history.cc6
-rw-r--r--chrome/browser/history/history.h4
-rw-r--r--chrome/browser/history/history_backend.cc7
-rw-r--r--chrome/browser/history/history_backend.h1
-rw-r--r--chrome/browser/resource_dispatcher_host.cc1
-rw-r--r--chrome/browser/views/download_item_view.cc369
-rw-r--r--chrome/browser/views/download_item_view.h47
-rw-r--r--chrome/browser/views/download_tab_view.cc190
-rw-r--r--chrome/browser/views/download_tab_view.h30
19 files changed, 904 insertions, 124 deletions
diff --git a/chrome/browser/download/download_exe.cc b/chrome/browser/download/download_exe.cc
index c9fdc4d..ce33781 100644
--- a/chrome/browser/download/download_exe.cc
+++ b/chrome/browser/download/download_exe.cc
@@ -59,6 +59,7 @@ static const wchar_t* const g_executables[] = {
L"app",
L"application",
L"asp",
+ L"asx",
L"bas",
L"bat",
L"chm",
@@ -66,6 +67,7 @@ static const wchar_t* const g_executables[] = {
L"com",
L"cpl",
L"crt",
+ L"dll",
L"exe",
L"fxp",
L"hlp",
@@ -73,6 +75,7 @@ static const wchar_t* const g_executables[] = {
L"inf",
L"ins",
L"isp",
+ L"jar",
L"js",
L"jse",
L"lnk",
diff --git a/chrome/browser/download/download_file.cc b/chrome/browser/download/download_file.cc
index 4fa3637..6b0fba4 100644
--- a/chrome/browser/download/download_file.cc
+++ b/chrome/browser/download/download_file.cc
@@ -578,3 +578,9 @@ void DownloadFileManager::CreateDirectory(const std::wstring& directory) {
if (!file_util::PathExists(directory))
file_util::CreateDirectory(directory);
}
+
+void DownloadFileManager::DeleteFile(const std::wstring& path) {
+ // Make sure we only delete files.
+ if (file_util::PathExists(path) && !file_util::DirectoryExists(path))
+ file_util::Delete(path, false);
+}
diff --git a/chrome/browser/download/download_file.h b/chrome/browser/download/download_file.h
index 5dc5d63..cce6398 100644
--- a/chrome/browser/download/download_file.h
+++ b/chrome/browser/download/download_file.h
@@ -210,6 +210,9 @@ class DownloadFileManager
// download directory exists.
void CreateDirectory(const std::wstring& directory);
+ // Called by the donwload manager to delete non validated dangerous downloads.
+ void DeleteFile(const std::wstring& path);
+
private:
// Timer helpers for updating the UI about the current progress of a download.
void StartUpdateTimer();
diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc
index d83ddc2..cff6935 100644
--- a/chrome/browser/download/download_manager.cc
+++ b/chrome/browser/download/download_manager.cc
@@ -15,6 +15,7 @@
#include "base/task.h"
#include "base/thread.h"
#include "base/timer.h"
+#include "base/rand_util.h"
#include "base/win_util.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_process.h"
@@ -97,6 +98,7 @@ static bool DownloadPathIsDangerous(const std::wstring& download_path) {
DownloadItem::DownloadItem(const DownloadCreateInfo& info)
: id_(-1),
full_path_(info.path),
+ original_name_(info.original_name),
url_(info.url),
total_bytes_(info.total_bytes),
received_bytes_(info.received_bytes),
@@ -105,6 +107,7 @@ DownloadItem::DownloadItem(const DownloadCreateInfo& info)
start_time_(info.start_time),
db_handle_(info.db_handle),
manager_(NULL),
+ safety_state_(SAFE),
is_paused_(false),
open_when_complete_(false),
render_process_id_(-1),
@@ -118,13 +121,16 @@ DownloadItem::DownloadItem(const DownloadCreateInfo& info)
DownloadItem::DownloadItem(int32 download_id,
const std::wstring& path,
const std::wstring& url,
+ const std::wstring& original_name,
const Time start_time,
int64 download_size,
int render_process_id,
- int request_id)
+ int request_id,
+ bool is_dangerous)
: id_(download_id),
full_path_(path),
url_(url),
+ original_name_(original_name),
total_bytes_(download_size),
received_bytes_(0),
start_tick_(GetTickCount()),
@@ -132,6 +138,7 @@ DownloadItem::DownloadItem(int32 download_id,
start_time_(start_time),
db_handle_(kUninitializedHandle),
manager_(NULL),
+ safety_state_(is_dangerous ? DANGEROUS : SAFE),
is_paused_(false),
open_when_complete_(false),
render_process_id_(render_process_id),
@@ -198,14 +205,16 @@ void DownloadItem::Cancel(bool update_history) {
void DownloadItem::Finished(int64 size) {
state_ = COMPLETE;
UpdateSize(size);
- UpdateObservers();
StopProgressTimer();
}
-void DownloadItem::Remove() {
+void DownloadItem::Remove(bool delete_on_disk) {
Cancel(true);
state_ = REMOVING;
+ if (delete_on_disk)
+ manager_->DeleteDownload(full_path_);
manager_->RemoveDownload(db_handle_);
+ // We are now deleted.
}
void DownloadItem::StartProgressTimer() {
@@ -255,6 +264,12 @@ void DownloadItem::TogglePause() {
UpdateObservers();
}
+std::wstring DownloadItem::GetFileName() const {
+ if (safety_state_ == DownloadItem::SAFE)
+ return file_name_;
+ return original_name_;
+}
+
// DownloadManager implementation ----------------------------------------------
// static
@@ -313,12 +328,19 @@ void DownloadManager::Shutdown() {
// 'in_progress_' may contain DownloadItems that have not finished the start
// complete (from the history service) and thus aren't in downloads_.
DownloadMap::iterator it = in_progress_.begin();
+ std::set<DownloadItem*> to_remove;
for (; it != in_progress_.end(); ++it) {
DownloadItem* download = it->second;
- if (download->state() == DownloadItem::IN_PROGRESS) {
- download->Cancel(false);
- UpdateHistoryForDownload(download);
+ if (download->safety_state() == DownloadItem::DANGEROUS) {
+ // Forget about any download that the user did not approve.
+ // Note that we cannot call download->Remove() this would invalidate our
+ // iterator.
+ to_remove.insert(download);
+ continue;
}
+ DCHECK_EQ(DownloadItem::IN_PROGRESS, download->state());
+ download->Cancel(false);
+ 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.
@@ -326,7 +348,25 @@ void DownloadManager::Shutdown() {
}
}
+ // 'dangerous_finished_' contains all complete downloads that have not been
+ // approved. They should be removed.
+ it = dangerous_finished_.begin();
+ for (; it != dangerous_finished_.end(); ++it)
+ to_remove.insert(it->second);
+
+ // Remove the dangerous download that are not approved.
+ for (std::set<DownloadItem*>::const_iterator rm_it = to_remove.begin();
+ rm_it != to_remove.end(); ++rm_it) {
+ DownloadItem* download = *rm_it;
+ download->Remove(true);
+ // Same as above, delete the download if it is not in 'downloads_'.
+ if (download->db_handle() == kUninitializedHandle)
+ delete download;
+ }
+ to_remove.clear();
+
in_progress_.clear();
+ dangerous_finished_.clear();
STLDeleteValues(&downloads_);
file_manager_ = NULL;
@@ -486,17 +526,36 @@ void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* 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)) {
+ std::wstring dir = file_util::GetDirectoryFromPath(info->suggested_path);
+ const std::wstring filename =
+ file_util::GetFilenameFromPath(info->suggested_path);
+ if (!file_util::PathIsWritable(dir)) {
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);
+ // If the download is deemmed dangerous, we'll use a temporary name for it.
+ if (!info->save_as && IsDangerous(filename)) {
+ info->original_name = file_util::GetFilenameFromPath(info->suggested_path);
+ // Create a temporary file to hold the file until the user approves its
+ // download.
+ std::wstring file_name;
+ std::wstring path;
+ while (path.empty()) {
+ SStringPrintf(&file_name, L"dangerous_download_%d.download",
+ base::RandInt(0, 100000));
+ path = dir;
+ file_util::AppendToPath(&path, file_name);
+ if (file_util::PathExists(path))
+ path.clear();
+ }
+ info->suggested_path = path;
+ info->is_dangerous = true;
+ }
+
// Now we return to the UI thread.
ui_loop_->PostTask(FROM_HERE,
NewRunnableMethod(this,
@@ -537,10 +596,12 @@ void DownloadManager::ContinueStartDownload(DownloadCreateInfo* info,
download = new DownloadItem(info->download_id,
info->path,
info->url,
+ info->original_name,
info->start_time,
info->total_bytes,
info->render_process_id,
- info->request_id);
+ info->request_id,
+ info->is_dangerous);
download->set_manager(this);
in_progress_[info->download_id] = download;
} else {
@@ -628,30 +689,7 @@ void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
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 {
+ if (it == in_progress_.end()) {
// 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
@@ -660,7 +698,102 @@ void DownloadManager::DownloadFinished(int32 download_id, int64 size) {
pending_finished_downloads_.find(download_id);
DCHECK(erase_it == pending_finished_downloads_.end());
pending_finished_downloads_[download_id] = size;
+ return;
+ }
+
+ // 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);
+
+ // 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);
+ }
+
+ // If this a dangerous download not yet validated by the user, don't do
+ // anything. When the user notifies us, it will trigger a call to
+ // ProceedWithFinishedDangerousDownload.
+ if (download->safety_state() == DownloadItem::DANGEROUS) {
+ dangerous_finished_[download_id] = download;
+ return;
+ }
+
+ if (download->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED) {
+ // We first need to rename the donwloaded file from its temporary name to
+ // its final name before we can continue.
+ file_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(
+ this, &DownloadManager::ProceedWithFinishedDangerousDownload,
+ download->db_handle(),
+ download->full_path(), download->original_name()));
+ return;
}
+ ContinueDownloadFinished(download);
+}
+
+void DownloadManager::ContinueDownloadFinished(DownloadItem* download) {
+ // If this was a dangerous download, it has now been approved and must be
+ // removed from dangerous_finished_ so it does not get deleted on shutdown.
+ DownloadMap::iterator it = dangerous_finished_.find(download->id());
+ if (it != dangerous_finished_.end())
+ dangerous_finished_.erase(it);
+
+ // Notify our observers that we are complete (the call to Finished() set the
+ // state to complete but did not notify).
+ download->UpdateObservers();
+
+ // 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);
+}
+
+// Called on the file thread. Renames the downloaded file to its original name.
+void DownloadManager::ProceedWithFinishedDangerousDownload(
+ int64 download_handle,
+ const std::wstring& path,
+ const std::wstring& original_name) {
+ bool success = false;
+ std::wstring new_path = path;
+ if (file_util::PathExists(path)) {
+ new_path = file_util::GetDirectoryFromPath(new_path);
+ file_util::AppendToPath(&new_path, original_name);
+ success = file_util::Move(path, new_path);
+ } else {
+ NOTREACHED();
+ }
+
+ ui_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &DownloadManager::DangerousDownloadRenamed,
+ download_handle, success, new_path));
+}
+
+// Call from the file thread when the finished dangerous download was renamed.
+void DownloadManager::DangerousDownloadRenamed(int64 download_handle,
+ bool success,
+ const std::wstring& new_path) {
+ DownloadMap::iterator it = downloads_.find(download_handle);
+ if (it == downloads_.end()) {
+ NOTREACHED();
+ return;
+ }
+
+ DownloadItem* download = it->second;
+ // If we failed to rename the file, we'll just keep the name as is.
+ if (success)
+ RenameDownload(download, new_path);
+
+ // Continue the download finished sequence.
+ ContinueDownloadFinished(download);
}
// static
@@ -741,6 +874,27 @@ void DownloadManager::OnPauseDownloadRequest(ResourceDispatcherHost* rdh,
rdh->PauseRequest(render_process_id, request_id, pause);
}
+bool DownloadManager::IsDangerous(const std::wstring& file_name) {
+ // TODO(jcampan): Improve me.
+ return IsExecutable(file_util::GetFileExtensionFromPath(file_name));
+}
+
+void DownloadManager::RenameDownload(DownloadItem* download,
+ const std::wstring& new_path) {
+ download->Rename(new_path);
+
+ // Update the history.
+
+ // No update necessary if the download was initiated while in incognito mode.
+ 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->UpdateDownloadPath(new_path, download->db_handle());
+}
+
void DownloadManager::RemoveDownload(int64 download_handle) {
DownloadMap::iterator it = downloads_.find(download_handle);
if (it == downloads_.end())
@@ -752,6 +906,9 @@ void DownloadManager::RemoveDownload(int64 download_handle) {
// Remove from our tables and delete.
downloads_.erase(it);
+ it = dangerous_finished_.find(download->id());
+ if (it != dangerous_finished_.end())
+ dangerous_finished_.erase(it);
delete download;
// Tell observers to refresh their views.
@@ -1014,6 +1171,29 @@ void DownloadManager::FileSelectionCanceled(void* params) {
info->download_id));
}
+void DownloadManager::DeleteDownload(const std::wstring& path) {
+ file_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ file_manager_, &DownloadFileManager::DeleteFile, path));
+}
+
+
+void DownloadManager::DangerousDownloadValidated(DownloadItem* download) {
+ DCHECK_EQ(DownloadItem::DANGEROUS, download->safety_state());
+ download->set_safety_state(DownloadItem::DANGEROUS_BUT_VALIDATED);
+ download->UpdateObservers();
+
+ // If the download is not complete, nothing to do. The required
+ // post-processing will be performed when it does complete.
+ if (download->state() != DownloadItem::COMPLETE)
+ return;
+
+ file_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &DownloadManager::ProceedWithFinishedDangerousDownload,
+ download->db_handle(), download->full_path(),
+ download->original_name()));
+}
+
// Operations posted to us from the history service ----------------------------
// The history service has retrieved all download entries. 'entries' contains
diff --git a/chrome/browser/download/download_manager.h b/chrome/browser/download/download_manager.h
index 0ab3dc1..6e78fb6 100644
--- a/chrome/browser/download/download_manager.h
+++ b/chrome/browser/download/download_manager.h
@@ -80,6 +80,12 @@ class DownloadItem {
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 {
@@ -94,10 +100,12 @@ class DownloadItem {
DownloadItem(int32 download_id,
const std::wstring& path,
const std::wstring& url,
+ const std::wstring& original_name,
const Time start_time,
int64 download_size,
int render_process_id,
- int request_id);
+ int request_id,
+ bool is_dangerous);
~DownloadItem();
@@ -125,12 +133,12 @@ class DownloadItem {
// when resuming a download (assuming the server supports byte ranges).
void Cancel(bool update_history);
- // Download operation completed
+ // Download operation completed.
void Finished(int64 size);
- // The user wants to remove the download from the views and history. This
- // operation does not delete the file on the disk.
- void Remove();
+ // 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();
@@ -158,8 +166,10 @@ class DownloadItem {
// Accessors
DownloadState state() const { return state_; }
- std::wstring full_path() const { return full_path_; }
std::wstring file_name() const { return file_name_; }
+ void set_file_name(const std::wstring& name) { file_name_ = name; }
+ std::wstring full_path() const { return full_path_; }
+ void set_full_path(const std::wstring& path) { full_path_ = path; }
std::wstring url() const { return url_; }
int64 total_bytes() const { return total_bytes_; }
void set_total_bytes(int64 total_bytes) { total_bytes_ = total_bytes; }
@@ -176,6 +186,16 @@ class DownloadItem {
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;
+ }
+ std::wstring original_name() const { return original_name_; }
+ void set_original_name(const std::wstring& 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.
+ std::wstring GetFileName() const;
private:
// Internal helper for maintaining consistent received and total sizes.
@@ -226,6 +246,14 @@ class DownloadItem {
// 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.
+ std::wstring original_name_;
+
// For canceling or pausing requests.
int render_process_id_;
int request_id_;
@@ -354,6 +382,12 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>,
virtual void FileSelected(const std::wstring& path, void* params);
virtual void FileSelectionCanceled(void* params);
+ // Deletes the specified path on the file thread.
+ void DeleteDownload(const std::wstring& path);
+
+ // Called when the user has validated the donwload of a dangerous file.
+ void DangerousDownloadValidated(DownloadItem* download);
+
private:
// Shutdown the download manager. This call is needed only after Init.
void Shutdown();
@@ -405,6 +439,32 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>,
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 std::wstring& path,
+ const std::wstring& original_name);
+
+ // Invoked on the UI thread when a dangerous downloaded file has been renamed.
+ void DangerousDownloadRenamed(int64 download_handle,
+ bool success,
+ const std::wstring& new_path);
+
+ // Checks whether a file represents a risk if downloaded.
+ bool IsDangerous(const std::wstring& file_name);
+
+ // Changes the paths and file name of the specified |download|, propagating
+ // the change to the history system.
+ void RenameDownload(DownloadItem* download, const std::wstring& 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
@@ -415,6 +475,12 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>,
// 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
@@ -426,6 +492,7 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>,
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_;
diff --git a/chrome/browser/download/download_util.cc b/chrome/browser/download/download_util.cc
index 530eade..96de46c 100644
--- a/chrome/browser/download/download_util.cc
+++ b/chrome/browser/download/download_util.cc
@@ -133,7 +133,7 @@ void BaseContextMenu::ExecuteCommand(int id) {
break;
}
case REMOVE_ITEM:
- download_->Remove();
+ download_->Remove(false);
break;
case CANCEL:
download_->Cancel(true);
diff --git a/chrome/browser/download/save_package.cc b/chrome/browser/download/save_package.cc
index 127fa53..ff56be2 100644
--- a/chrome/browser/download/save_package.cc
+++ b/chrome/browser/download/save_package.cc
@@ -126,7 +126,7 @@ SavePackage::~SavePackage() {
// We call this to remove the view from the shelf. It will invoke
// DownloadManager::RemoveDownload, but since the fake DownloadItem is not
// owned by DownloadManager, it will do nothing to our fake item.
- download_->Remove();
+ download_->Remove(false);
delete download_;
download_ = NULL;
}
@@ -174,8 +174,8 @@ bool SavePackage::Init() {
}
// Create the fake DownloadItem and display the view.
- download_ = new DownloadItem(1, saved_main_file_path_,
- page_url_, Time::Now(), 0, -1, -1);
+ download_ = new DownloadItem(1, saved_main_file_path_, page_url_,
+ std::wstring(), Time::Now(), 0, -1, -1, false);
download_->set_manager(web_contents_->profile()->GetDownloadManager());
DownloadShelfView* shelf = web_contents_->GetDownloadShelfView();
shelf->AddDownloadView(new DownloadItemView(
diff --git a/chrome/browser/history/download_database.cc b/chrome/browser/history/download_database.cc
index 1baf976..e604e5d 100644
--- a/chrome/browser/history/download_database.cc
+++ b/chrome/browser/history/download_database.cc
@@ -93,6 +93,20 @@ bool DownloadDatabase::UpdateDownload(int64 received_bytes,
return statement->step() == SQLITE_DONE;
}
+bool DownloadDatabase::UpdateDownloadPath(const std::wstring& path,
+ DownloadID db_handle) {
+ DCHECK(db_handle > 0);
+ SQLITE_UNIQUE_STATEMENT(statement, GetStatementCache(),
+ "UPDATE downloads "
+ "SET full_path=? WHERE id=?");
+ if (!statement.is_valid())
+ return false;
+
+ statement->bind_wstring(0, path);
+ statement->bind_int64(1, db_handle);
+ return statement->step() == SQLITE_DONE;
+}
+
int64 DownloadDatabase::CreateDownload(const DownloadCreateInfo& info) {
SQLITE_UNIQUE_STATEMENT(statement, GetStatementCache(),
"INSERT INTO downloads "
diff --git a/chrome/browser/history/download_database.h b/chrome/browser/history/download_database.h
index 1fc9812..d733546 100644
--- a/chrome/browser/history/download_database.h
+++ b/chrome/browser/history/download_database.h
@@ -27,6 +27,9 @@ class DownloadDatabase {
// Update the state of one download. Returns true if successful.
bool UpdateDownload(int64 received_bytes, int32 state, DownloadID db_handle);
+ // Update the path of one download. Returns true if successful.
+ bool UpdateDownloadPath(const std::wstring& path, DownloadID db_handle);
+
// Create a new database entry for one download and return its primary db id.
int64 CreateDownload(const DownloadCreateInfo& info);
diff --git a/chrome/browser/history/download_types.h b/chrome/browser/history/download_types.h
index 16f3ad7..5b6e5b4 100644
--- a/chrome/browser/history/download_types.h
+++ b/chrome/browser/history/download_types.h
@@ -37,7 +37,8 @@ struct DownloadCreateInfo {
render_view_id(-1),
request_id(-1),
db_handle(0),
- save_as(false) {
+ save_as(false),
+ is_dangerous(false) {
}
DownloadCreateInfo() : download_id(-1) {}
@@ -59,6 +60,10 @@ struct DownloadCreateInfo {
std::string content_disposition;
std::string mime_type;
bool save_as;
+ // Whether this download is potentially dangerous (ex: exe, dll, ...).
+ bool is_dangerous;
+ // The original name for a dangerous download.
+ std::wstring original_name;
};
#endif // CHROME_BROWSER_DOWNLOAD_TYPES_H__
diff --git a/chrome/browser/history/history.cc b/chrome/browser/history/history.cc
index db04446..92bea22 100644
--- a/chrome/browser/history/history.cc
+++ b/chrome/browser/history/history.cc
@@ -469,6 +469,12 @@ void HistoryService::UpdateDownload(int64 received_bytes,
received_bytes, state, db_handle);
}
+void HistoryService::UpdateDownloadPath(const std::wstring& path,
+ int64 db_handle) {
+ ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownloadPath,
+ path, db_handle);
+}
+
void HistoryService::RemoveDownload(int64 db_handle) {
ScheduleAndForget(PRIORITY_NORMAL,
&HistoryBackend::RemoveDownload, db_handle);
diff --git a/chrome/browser/history/history.h b/chrome/browser/history/history.h
index 74ef67b..2aee029 100644
--- a/chrome/browser/history/history.h
+++ b/chrome/browser/history/history.h
@@ -421,6 +421,10 @@ class HistoryService : public CancelableRequestProvider,
// the database with no need for a callback.
void UpdateDownload(int64 received_bytes, int32 state, int64 db_handle);
+ // Called to update the history service about the path of a download.
+ // This is a 'fire and forget' query.
+ void UpdateDownloadPath(const std::wstring& path, int64 db_handle);
+
// Permanently remove a download from the history system. This is a 'fire and
// forget' operation.
void RemoveDownload(int64 db_handle);
diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc
index 21f77fb..f8e6449 100644
--- a/chrome/browser/history/history_backend.cc
+++ b/chrome/browser/history/history_backend.cc
@@ -934,6 +934,13 @@ void HistoryBackend::UpdateDownload(int64 received_bytes,
db_->UpdateDownload(received_bytes, state, db_handle);
}
+// Update the path of a particular download entry.
+void HistoryBackend::UpdateDownloadPath(const std::wstring& path,
+ int64 db_handle) {
+ if (db_.get())
+ db_->UpdateDownloadPath(path, db_handle);
+}
+
// Create a new download entry and pass back the db_handle to it.
void HistoryBackend::CreateDownload(
scoped_refptr<DownloadCreateRequest> request,
diff --git a/chrome/browser/history/history_backend.h b/chrome/browser/history/history_backend.h
index dd37c3e..e646570 100644
--- a/chrome/browser/history/history_backend.h
+++ b/chrome/browser/history/history_backend.h
@@ -193,6 +193,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
void QueryDownloads(scoped_refptr<DownloadQueryRequest> request);
void UpdateDownload(int64 received_bytes, int32 state, int64 db_handle);
+ void UpdateDownloadPath(const std::wstring& path, int64 db_handle);
void CreateDownload(scoped_refptr<DownloadCreateRequest> request,
const DownloadCreateInfo& info);
void RemoveDownload(int64 db_handle);
diff --git a/chrome/browser/resource_dispatcher_host.cc b/chrome/browser/resource_dispatcher_host.cc
index e6035f8..4b0f37c 100644
--- a/chrome/browser/resource_dispatcher_host.cc
+++ b/chrome/browser/resource_dispatcher_host.cc
@@ -302,6 +302,7 @@ class ResourceDispatcherHost::DownloadEventHandler
info->content_disposition = content_disposition_;
info->mime_type = response->response_head.mime_type;
info->save_as = save_as_;
+ info->is_dangerous = false;
download_manager_->file_loop()->PostTask(FROM_HERE,
NewRunnableMethod(download_manager_,
&DownloadFileManager::StartDownload,
diff --git a/chrome/browser/views/download_item_view.cc b/chrome/browser/views/download_item_view.cc
index 2dcba5a..6d0af1e3 100644
--- a/chrome/browser/views/download_item_view.cc
+++ b/chrome/browser/views/download_item_view.cc
@@ -6,6 +6,8 @@
#include <vector>
+#include "base/file_util.h"
+#include "base/string_util.h"
#include "chrome/app/theme/theme_resources.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_util.h"
@@ -14,6 +16,7 @@
#include "chrome/common/l10n_util.h"
#include "chrome/common/resource_bundle.h"
#include "chrome/common/win_util.h"
+#include "chrome/views/native_button.h"
#include "chrome/views/root_view.h"
#include "chrome/views/view_container.h"
@@ -23,15 +26,27 @@
// animation is added, and also possibly to take into account
// different screen resolutions.
static const int kTextWidth = 140; // Pixels
+static const int kDangerousTextWidth = 200; // Pixels
static const int kHorizontalTextPadding = 2; // Pixels
static const int kVerticalPadding = 3; // Pixels
static const int kVerticalTextSpacer = 2; // Pixels
static const int kVerticalTextPadding = 2; // Pixels
+// The maximum number of characters we show in a file name when displaying the
+// dangerous download message.
+static const int kFileNameMaxLength = 20;
+
// We add some padding before the left image so that the progress animation icon
// hides the corners of the left image.
static const int kLeftPadding = 0; // Pixels.
+// The space between the Save and Discard buttons when prompting for a dangerous
+// donwload.
+static const int kButtonPadding = 5; // Pixels.
+
+// The space on the left and right side of the dangerous donwload label.
+static const int kLabelPadding = 4; // Pixels.
+
static const SkColor kFileNameColor = SkColorSetRGB(87, 108, 149);
static const SkColor kStatusColor = SkColorSetRGB(123, 141, 174);
@@ -50,11 +65,16 @@ DownloadItemView::DownloadItemView(DownloadItem* download,
body_state_(NORMAL),
drop_down_state_(NORMAL),
drop_down_pressed_(false),
- file_name_(download_->file_name()),
status_text_(l10n_util::GetString(IDS_DOWNLOAD_STATUS_STARTING)),
show_status_text_(true),
dragging_(false),
- starting_drag_(false) {
+ starting_drag_(false),
+ warning_icon_(NULL),
+ save_button_(NULL),
+ discard_button_(NULL),
+ dangerous_download_label_(NULL),
+ dangerous_download_label_sized_(false),
+ cached_button_size_(0, 0) {
// TODO(idana) Bug# 1163334
//
// We currently do not mirror each download item on the download shelf (even
@@ -131,6 +151,19 @@ DownloadItemView::DownloadItemView(DownloadItem* download,
};
pushed_drop_down_image_set_ = pushed_drop_down_image_set;
+ BodyImageSet dangerous_mode_body_image_set = {
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_TOP),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_TOP),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_TOP_NO_DD),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_NO_DD),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_NO_DD)
+ };
+ dangerous_mode_body_image_set_ = dangerous_mode_body_image_set;
+
LoadIcon();
font_ = ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
@@ -152,6 +185,33 @@ DownloadItemView::DownloadItemView(DownloadItem* download,
body_hover_animation_.reset(new SlideAnimation(this));
drop_hover_animation_.reset(new SlideAnimation(this));
+ if (download->safety_state() == DownloadItem::DANGEROUS) {
+ body_state_ = DANGEROUS;
+ drop_down_state_ = DANGEROUS;
+
+ warning_icon_ = rb.GetBitmapNamed(IDR_WARNING);
+ save_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_SAVE_DOWNLOAD));
+ save_button_->set_enforce_dlu_min_size(false);
+ save_button_->SetListener(this);
+ discard_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_DISCARD_DOWNLOAD));
+ discard_button_->SetListener(this);
+ discard_button_->set_enforce_dlu_min_size(false);
+ AddChildView(save_button_);
+ AddChildView(discard_button_);
+ std::wstring file_name = download->original_name();
+ // Ensure the file name is not too long.
+ ElideString(file_name, kFileNameMaxLength, &file_name);
+ dangerous_download_label_ = new ChromeViews::Label(
+ l10n_util::GetStringF(IDS_PROMPT_DANGEROUS_DOWNLOAD, file_name));
+ dangerous_download_label_->SetMultiLine(true);
+ dangerous_download_label_->SetHorizontalAlignment(
+ ChromeViews::Label::ALIGN_LEFT);
+ dangerous_download_label_->SetColor(kFileNameColor);
+ AddChildView(dangerous_download_label_);
+ }
+
// Set up our animation
StartDownloadProgress();
}
@@ -190,6 +250,12 @@ void DownloadItemView::StopDownloadProgress() {
void DownloadItemView::OnDownloadUpdated(DownloadItem* download) {
DCHECK(download == download_);
+ if (body_state_ == DANGEROUS &&
+ download->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED) {
+ // We have been approved.
+ ClearDangerousMode();
+ }
+
std::wstring status_text = model_->GetStatusText();
switch (download_->state()) {
case DownloadItem::IN_PROGRESS:
@@ -227,18 +293,46 @@ void DownloadItemView::OnDownloadUpdated(DownloadItem* download) {
// View overrides
+// In dangerous mode we have to layout our buttons.
+void DownloadItemView::Layout() {
+ if (IsDangerousMode()) {
+ SizeLabelToMinWidth();
+ int x = kLeftPadding + dangerous_mode_body_image_set_.top_left->width() +
+ warning_icon_->width() + kLabelPadding;
+ int y = (height() - dangerous_download_label_->height()) / 2;
+ dangerous_download_label_->SetBounds(x, y,
+ dangerous_download_label_->width(),
+ dangerous_download_label_->height());
+ CSize button_size;
+ GetButtonSize(&button_size);
+ x += dangerous_download_label_->width() + kLabelPadding;
+ y = (height() - button_size.cy) / 2;
+ save_button_->SetBounds(x, y, button_size.cx, button_size.cy);
+ x += button_size.cx + kButtonPadding;
+ discard_button_->SetBounds(x, y, button_size.cx, button_size.cy);
+ }
+}
+
+void DownloadItemView::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void DownloadItemView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == discard_button_) {
+ if (download_->state() == DownloadItem::IN_PROGRESS)
+ download_->Cancel(true);
+ download_->Remove(true);
+ // WARNING: we are deleted at this point. Don't access 'this'.
+ } else if (sender == save_button_) {
+ // This will change the state and notify us.
+ download_->manager()->DangerousDownloadValidated(download_);
+ }
+}
+
// Load an icon for the file type we're downloading, and animate any in progress
// download state.
void DownloadItemView::Paint(ChromeCanvas* canvas) {
- int center_width = width() - kLeftPadding -
- normal_body_image_set_.left->width() -
- normal_body_image_set_.right->width() -
- normal_drop_down_image_set_.center->width();
-
- // May be caused by animation.
- if (center_width <= 0)
- return;
-
BodyImageSet* body_image_set;
switch (body_state_) {
case NORMAL:
@@ -248,6 +342,9 @@ void DownloadItemView::Paint(ChromeCanvas* canvas) {
case PUSHED:
body_image_set = &pushed_body_image_set_;
break;
+ case DANGEROUS:
+ body_image_set = &dangerous_mode_body_image_set_;
+ break;
default:
NOTREACHED();
}
@@ -260,10 +357,24 @@ void DownloadItemView::Paint(ChromeCanvas* canvas) {
case PUSHED:
drop_down_image_set = &pushed_drop_down_image_set_;
break;
+ case DANGEROUS:
+ drop_down_image_set = NULL; // No drop-down in dangerous mode.
+ break;
default:
NOTREACHED();
}
+ int center_width = width() - kLeftPadding -
+ body_image_set->left->width() -
+ body_image_set->right->width() -
+ (drop_down_image_set ?
+ normal_drop_down_image_set_.center->width() :
+ 0);
+
+ // May be caused by animation.
+ if (center_width <= 0)
+ return;
+
// Paint the background images.
int x = kLeftPadding;
PaintBitmaps(canvas,
@@ -302,65 +413,76 @@ void DownloadItemView::Paint(ChromeCanvas* canvas) {
PaintBitmaps(canvas,
hot_body_image_set_.top_right, hot_body_image_set_.right,
hot_body_image_set_.bottom_right,
- x, box_y_, box_height_, hot_body_image_set_.top_right->width());
+ x, box_y_, box_height_,
+ hot_body_image_set_.top_right->width());
canvas->restore();
}
x += body_image_set->top_right->width();
- PaintBitmaps(canvas,
- drop_down_image_set->top, drop_down_image_set->center,
- drop_down_image_set->bottom,
- x, box_y_, box_height_, drop_down_image_set->top->width());
-
- // Overlay our drop-down hot state.
- if (drop_hover_animation_->GetCurrentValue() > 0) {
- canvas->saveLayerAlpha(NULL,
- static_cast<int>(drop_hover_animation_->GetCurrentValue() * 255),
- SkCanvas::kARGB_NoClipLayer_SaveFlag);
- canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode);
+ // Paint the drop-down.
+ if (drop_down_image_set) {
PaintBitmaps(canvas,
drop_down_image_set->top, drop_down_image_set->center,
drop_down_image_set->bottom,
x, box_y_, box_height_, drop_down_image_set->top->width());
- canvas->restore();
+ // Overlay our drop-down hot state.
+ if (drop_hover_animation_->GetCurrentValue() > 0) {
+ canvas->saveLayerAlpha(NULL,
+ static_cast<int>(drop_hover_animation_->GetCurrentValue() * 255),
+ SkCanvas::kARGB_NoClipLayer_SaveFlag);
+ canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode);
+
+ PaintBitmaps(canvas,
+ drop_down_image_set->top, drop_down_image_set->center,
+ drop_down_image_set->bottom,
+ x, box_y_, box_height_, drop_down_image_set->top->width());
+
+ canvas->restore();
+ }
}
// Print the text, left aligned.
// Last value of x was the end of the right image, just before the button.
- if (show_status_text_) {
- int y = box_y_ + kVerticalPadding;
- canvas->DrawStringInt(file_name_, font_, kFileNameColor,
- download_util::kSmallProgressIconSize, y,
- kTextWidth, font_.height());
- y += font_.height() + kVerticalTextPadding;
-
- canvas->DrawStringInt(status_text_, font_, kStatusColor,
- download_util::kSmallProgressIconSize, y,
- kTextWidth, font_.height());
- } else {
- int y = box_y_ + (box_height_ - font_.height()) / 2;
- canvas->DrawStringInt(file_name_, font_, kFileNameColor,
- download_util::kSmallProgressIconSize, y,
- kTextWidth, font_.height());
+ // Note that in dangerous mode we use a label (as the text is multi-line).
+ if (!IsDangerousMode()) {
+ if (show_status_text_) {
+ int y = box_y_ + kVerticalPadding;
+ canvas->DrawStringInt(download_->GetFileName(), font_, kFileNameColor,
+ download_util::kSmallProgressIconSize, y,
+ kTextWidth, font_.height());
+ y += font_.height() + kVerticalTextPadding;
+
+ canvas->DrawStringInt(status_text_, font_, kStatusColor,
+ download_util::kSmallProgressIconSize, y,
+ kTextWidth, font_.height());
+ } else {
+ int y = box_y_ + (box_height_ - font_.height()) / 2;
+ canvas->DrawStringInt(download_->GetFileName(), font_, kFileNameColor,
+ download_util::kSmallProgressIconSize, y,
+ kTextWidth, font_.height());
+ }
}
// Paint the icon.
IconManager* im = g_browser_process->icon_manager();
- SkBitmap* icon = im->LookupIcon(download_->full_path(), IconLoader::SMALL);
+ SkBitmap* icon = IsDangerousMode() ? warning_icon_ :
+ im->LookupIcon(download_->full_path(), IconLoader::SMALL);
if (icon) {
- if (download_->state() == DownloadItem::IN_PROGRESS) {
- download_util::PaintDownloadProgress(canvas, this, 0, 0,
- progress_angle_,
- download_->PercentComplete(),
- download_util::SMALL);
- } else if (download_->state() == DownloadItem::COMPLETE &&
- complete_animation_->IsAnimating()) {
- download_util::PaintDownloadComplete(canvas, this, 0, 0,
- complete_animation_->GetCurrentValue(),
- download_util::SMALL);
+ if (!IsDangerousMode()) {
+ if (download_->state() == DownloadItem::IN_PROGRESS) {
+ download_util::PaintDownloadProgress(canvas, this, 0, 0,
+ progress_angle_,
+ download_->PercentComplete(),
+ download_util::SMALL);
+ } else if (download_->state() == DownloadItem::COMPLETE &&
+ complete_animation_->IsAnimating()) {
+ download_util::PaintDownloadComplete(canvas, this, 0, 0,
+ complete_animation_->GetCurrentValue(),
+ download_util::SMALL);
+ }
}
// Draw the icon image
@@ -401,20 +523,65 @@ void DownloadItemView::SetState(State body_state, State drop_down_state) {
SchedulePaint();
}
-void DownloadItemView::GetPreferredSize(CSize* out) {
- int width = kLeftPadding + normal_body_image_set_.top_left->width();
- width += download_util::kSmallProgressIconSize;
- width += kTextWidth;
- width += normal_body_image_set_.top_right->width();
- width += normal_drop_down_image_set_.top->width();
+void DownloadItemView::ClearDangerousMode() {
+ DCHECK(download_->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED &&
+ body_state_ == DANGEROUS && drop_down_state_ == DANGEROUS);
+
+ body_state_ = NORMAL;
+ drop_down_state_ = NORMAL;
+
+ // Remove the views used by the dangerours mode.
+ RemoveChildView(save_button_);
+ delete save_button_;
+ save_button_ = NULL;
+ RemoveChildView(discard_button_);
+ delete discard_button_;
+ discard_button_ = NULL;
+ RemoveChildView(dangerous_download_label_);
+ delete dangerous_download_label_;
+ dangerous_download_label_ = NULL;
+
+ // We need to load the icon now that the download_ has the real path.
+ LoadIcon();
+
+ // Force the shelf to layout again as our size has changed.
+ parent_->Layout();
+ parent_->SchedulePaint();
+}
+void DownloadItemView::GetPreferredSize(CSize* out) {
+ int width, height;
+ if (IsDangerousMode()) {
+ width = kLeftPadding + dangerous_mode_body_image_set_.top_left->width();
+ width += warning_icon_->width() + kLabelPadding;
+ width += dangerous_download_label_->width() + kLabelPadding;
+ CSize button_size;
+ GetButtonSize(&button_size);
+ width += button_size.cx * 2 + kButtonPadding;
+ width += dangerous_mode_body_image_set_.top_right->width();
+ height = std::max<int>(2 * kVerticalPadding + 2 * font_.height() +
+ kVerticalTextPadding,
+ 2 * kVerticalPadding + warning_icon_->height());
+ height = std::max<int>(height, 2 * kVerticalPadding + button_size.cy);
+ } else {
+ width = kLeftPadding + normal_body_image_set_.top_left->width();
+ width += download_util::kSmallProgressIconSize;
+ width += kTextWidth;
+ width += normal_body_image_set_.top_right->width();
+ width += normal_drop_down_image_set_.top->width();
+ height = std::max<int>(2 * kVerticalPadding + 2 * font_.height() +
+ kVerticalTextPadding,
+ download_util::kSmallProgressIconSize);
+ }
out->cx = width;
- out->cy = std::max<int>(
- 2 * kVerticalPadding + 2 * font_.height() + kVerticalTextPadding,
- download_util::kSmallProgressIconSize);
+ out->cy = height;
}
void DownloadItemView::OnMouseExited(const ChromeViews::MouseEvent& event) {
+ // Mouse should not activate us in dangerous mode.
+ if (IsDangerousMode())
+ return;
+
SetState(NORMAL, drop_down_pressed_ ? PUSHED : NORMAL);
body_hover_animation_->Hide();
drop_hover_animation_->Hide();
@@ -422,6 +589,10 @@ void DownloadItemView::OnMouseExited(const ChromeViews::MouseEvent& event) {
// Display the context menu for this item.
bool DownloadItemView::OnMousePressed(const ChromeViews::MouseEvent& event) {
+ // Mouse should not activate us in dangerous mode.
+ if (IsDangerousMode())
+ return true;
+
// Stop any completion animation.
if (complete_animation_.get() && complete_animation_->IsAnimating())
complete_animation_->End();
@@ -472,6 +643,10 @@ bool DownloadItemView::OnMousePressed(const ChromeViews::MouseEvent& event) {
}
void DownloadItemView::OnMouseMoved(const ChromeViews::MouseEvent& event) {
+ // Mouse should not activate us in dangerous mode.
+ if (IsDangerousMode())
+ return;
+
bool on_body = event.x() < drop_down_x_;
SetState(on_body ? HOT : NORMAL, on_body ? NORMAL : HOT);
if (on_body) {
@@ -485,6 +660,10 @@ void DownloadItemView::OnMouseMoved(const ChromeViews::MouseEvent& event) {
void DownloadItemView::OnMouseReleased(const ChromeViews::MouseEvent& event,
bool canceled) {
+ // Mouse should not activate us in dangerous mode.
+ if (IsDangerousMode())
+ return;
+
if (dragging_) {
// Starting a drag results in a MouseReleased, we need to ignore it.
dragging_ = false;
@@ -499,6 +678,10 @@ void DownloadItemView::OnMouseReleased(const ChromeViews::MouseEvent& event,
// Handle drag (file copy) operations.
bool DownloadItemView::OnMouseDragged(const ChromeViews::MouseEvent& event) {
+ // Mouse should not activate us in dangerous mode.
+ if (IsDangerousMode())
+ return true;
+
if (!starting_drag_) {
starting_drag_ = true;
drag_start_point_ = event.location();
@@ -544,3 +727,71 @@ void DownloadItemView::LoadIcon() {
NewCallback(this, &DownloadItemView::OnExtractIconComplete));
}
+void DownloadItemView::GetButtonSize(CSize* size) {
+ DCHECK(save_button_ && discard_button_);
+ // We cache the size when successfully retrieved, not for performance reasons
+ // but because if this DownloadItemView is being animated while the tab is
+ // not showing, the native buttons are not parented and their preferred size
+ // is 0, messing-up the layout.
+ if (cached_button_size_.cx != 0) {
+ *size = cached_button_size_;
+ }
+
+ CSize tmp_size;
+ save_button_->GetMinimumSize(size);
+ discard_button_->GetMinimumSize(&tmp_size);
+
+ size->cx = std::max(size->cx, tmp_size.cx);
+ size->cy = std::max(size->cy, tmp_size.cy);
+
+ if (size->cx != 0) {
+ cached_button_size_.cx = size->cx;
+ cached_button_size_.cy = size->cy;
+ }
+}
+
+// This method computes the miminum width of the label for diplaying its text
+// on 2 lines. It just breaks the string in 2 lines on the spaces and keeps the
+// configuration with minimum width.
+void DownloadItemView::SizeLabelToMinWidth() {
+ if (dangerous_download_label_sized_)
+ return;
+
+ std::wstring text = dangerous_download_label_->GetText();
+ TrimWhitespace(text, TRIM_ALL, &text);
+ DCHECK_EQ(std::wstring::npos, text.find(L"\n"));
+
+ // Make the label big so that GetPreferredSize() is not constrained by the
+ // current width.
+ dangerous_download_label_->SetBounds(0, 0, 1000, 1000);
+
+ CSize size(0, 0);
+ int min_width = -1;
+ int sp_index = text.find(L" ");
+ while (sp_index != std::wstring::npos) {
+ text.replace(sp_index, 1, L"\n");
+ dangerous_download_label_->SetText(text);
+ dangerous_download_label_->GetPreferredSize(&size);
+
+ if (min_width == -1)
+ min_width = size.cx;
+
+ // If thw width is growing again, it means we passed the optimal width spot.
+ if (size.cx > min_width)
+ break;
+ else
+ min_width = size.cx;
+
+ // Restore the string.
+ text.replace(sp_index, 1, L" ");
+
+ sp_index = text.find(L" ", sp_index + 1);
+ }
+
+ // If we have a line with no space, we won't cut it.
+ if (min_width == -1)
+ dangerous_download_label_->GetPreferredSize(&size);
+
+ dangerous_download_label_->SetBounds(0, 0, size.cx, size.cy);
+ dangerous_download_label_sized_ = true;
+} \ No newline at end of file
diff --git a/chrome/browser/views/download_item_view.h b/chrome/browser/views/download_item_view.h
index 69adcc4..c29a154 100644
--- a/chrome/browser/views/download_item_view.h
+++ b/chrome/browser/views/download_item_view.h
@@ -26,13 +26,17 @@
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/icon_manager.h"
#include "chrome/views/event.h"
+#include "chrome/views/native_button.h"
#include "chrome/views/view.h"
-#include "chrome/views/label.h"
+namespace ChromeViews {
+ class Label;
+}
class DownloadShelfView;
class SkBitmap;
-class DownloadItemView : public ChromeViews::View,
+class DownloadItemView : public ChromeViews::NativeButton::Listener,
+ public ChromeViews::View,
public DownloadItem::Observer,
public AnimationDelegate {
public:
@@ -56,6 +60,7 @@ class DownloadItemView : public ChromeViews::View,
virtual void OnDownloadUpdated(DownloadItem* download);
// View overrides
+ virtual void Layout();
virtual void Paint(ChromeCanvas* canvas);
virtual void GetPreferredSize(CSize *out);
virtual void OnMouseExited(const ChromeViews::MouseEvent& event);
@@ -64,6 +69,10 @@ class DownloadItemView : public ChromeViews::View,
virtual void OnMouseReleased(const ChromeViews::MouseEvent& event,
bool canceled);
virtual bool OnMouseDragged(const ChromeViews::MouseEvent& event);
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+
+ // NativeButton::Listener implementation.
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
// AnimationDelegate implementation.
virtual void AnimationProgressed(const Animation* animation);
@@ -81,6 +90,7 @@ class DownloadItemView : public ChromeViews::View,
NORMAL = 0,
HOT,
PUSHED,
+ DANGEROUS
};
// The image set associated with the part containing the icon and text.
@@ -121,14 +131,33 @@ class DownloadItemView : public ChromeViews::View,
// Sets the state and triggers a repaint.
void SetState(State body_state, State drop_down_state);
+ // Whether we are in the dangerous mode.
+ bool IsDangerousMode() { return body_state_ == DANGEROUS; }
+
+ // Reverts from dangerous mode to normal download mode.
+ void ClearDangerousMode();
+
+ // Sets |size| with the size of the Save and Discard buttons (they have the
+ // same size).
+ void GetButtonSize(CSize* size);
+
+ // Sizes the dangerous download label to a minimum width available using 2
+ // lines. The size is computed only the first time this method is invoked
+ // and simply returned on subsequent calls.
+ void SizeLabelToMinWidth();
+
// The different images used for the background.
BodyImageSet normal_body_image_set_;
BodyImageSet hot_body_image_set_;
BodyImageSet pushed_body_image_set_;
+ BodyImageSet dangerous_mode_body_image_set_;
DropDownImageSet normal_drop_down_image_set_;
DropDownImageSet hot_drop_down_image_set_;
DropDownImageSet pushed_drop_down_image_set_;
+ // The warning icon showns for dangerous downloads.
+ SkBitmap* warning_icon_;
+
// The model we query for display information
DownloadItem* download_;
@@ -136,7 +165,6 @@ class DownloadItemView : public ChromeViews::View,
DownloadShelfView* parent_;
// Elements of our particular download
- std::wstring file_name_;
std::wstring status_text_;
bool show_status_text_;
@@ -189,6 +217,19 @@ class DownloadItemView : public ChromeViews::View,
// Progress animation
base::RepeatingTimer<DownloadItemView> progress_timer_;
+ // Dangerous mode buttons.
+ ChromeViews::NativeButton* save_button_;
+ ChromeViews::NativeButton* discard_button_;
+
+ // Dangerous mode label.
+ ChromeViews::Label* dangerous_download_label_;
+
+ // Whether the dangerous mode label has been sized yet.
+ bool dangerous_download_label_sized_;
+
+ // The size of the buttons. Cached so animation works when hidden.
+ CSize cached_button_size_;
+
DISALLOW_EVIL_CONSTRUCTORS(DownloadItemView);
};
diff --git a/chrome/browser/views/download_tab_view.cc b/chrome/browser/views/download_tab_view.cc
index 62e5668..679aa54 100644
--- a/chrome/browser/views/download_tab_view.cc
+++ b/chrome/browser/views/download_tab_view.cc
@@ -30,7 +30,8 @@
// Approximate spacing, in pixels, taken from initial UI mock up screens
static const int kVerticalPadding = 5;
-static const int kHorizontalButtonPadding = 15;
+static const int kHorizontalLinkPadding = 15;
+static const int kHorizontalButtonPadding = 8;
// For vertical and horizontal element spacing
static const int kSpacer = 20;
@@ -65,12 +66,19 @@ static const SkColor kUrlColor = SkColorSetRGB(0, 128, 0);
// Paused download indicator (red)
static const SkColor kPauseColor = SkColorSetRGB(128, 0, 0);
+// Warning label color (blue)
+static const SkColor kWarningColor = SkColorSetRGB(87, 108, 149);
+
// Selected item background color
static const SkColor kSelectedItemColor = SkColorSetRGB(215, 232, 255);
// State key used to identify search text.
static const wchar_t kSearchTextKey[] = L"st";
+// The maximum number of characters we show in a file name when displaying the
+// dangerous download message.
+static const int kFileNameMaxLength = 20;
+
// Sorting functor for DownloadItem --------------------------------------------
// Sort DownloadItems into ascending order by their start time.
@@ -87,7 +95,8 @@ class DownloadItemSorter : public std::binary_function<DownloadItem*,
// DownloadItemTabView implementation ------------------------------------------
DownloadItemTabView::DownloadItemTabView()
: model_(NULL),
- parent_(NULL) {
+ parent_(NULL),
+ is_floating_view_renderer_(false) {
// Create our element views using empty strings for now,
// set them based on the model's state in Layout().
since_ = new ChromeViews::Label(L"");
@@ -111,6 +120,29 @@ DownloadItemTabView::DownloadItemTabView()
file_name_->SetFont(font);
AddChildView(file_name_);
+ // dangerous_download_warning_ is enabled when a dangerous download has been
+ // initiated.
+ dangerous_download_warning_ = new ChromeViews::Label();
+ dangerous_download_warning_ ->SetMultiLine(true);
+ dangerous_download_warning_->SetColor(kWarningColor);
+ dangerous_download_warning_->SetHorizontalAlignment(
+ ChromeViews::Label::ALIGN_LEFT);
+ dangerous_download_warning_->SetFont(font);
+ AddChildView(dangerous_download_warning_);
+
+ // The save and discard buttons are shown to prompt the user when a dangerous
+ // download was started.
+ save_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_SAVE_DOWNLOAD));
+ save_button_->set_enforce_dlu_min_size(false);
+ save_button_->SetListener(this);
+ discard_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_DISCARD_DOWNLOAD));
+ discard_button_->SetListener(this);
+ discard_button_->set_enforce_dlu_min_size(false);
+ AddChildView(save_button_);
+ AddChildView(discard_button_);
+
// Set our URL name
download_url_ = new ChromeViews::Label(L"");
download_url_->SetColor(kUrlColor);
@@ -172,9 +204,9 @@ void DownloadItemTabView::GetPreferredSize(CSize* out) {
out->cx = download_util::kBigProgressIconSize +
2 * kSpacer +
- kHorizontalButtonPadding +
+ kHorizontalLinkPadding +
kFilenameSize +
- std::max(pause_size.cx + cancel_size.cx + kHorizontalButtonPadding,
+ std::max(pause_size.cx + cancel_size.cx + kHorizontalLinkPadding,
show_size.cx);
out->cy = download_util::kBigProgressIconSize;
@@ -188,13 +220,19 @@ void DownloadItemTabView::Layout() {
DCHECK(model_);
switch (model_->state()) {
case DownloadItem::COMPLETE:
- LayoutComplete();
+ if (model_->safety_state() == DownloadItem::DANGEROUS)
+ LayoutPromptDangerousDownload();
+ else
+ LayoutComplete();
break;
case DownloadItem::CANCELLED:
LayoutCancelled();
break;
case DownloadItem::IN_PROGRESS:
- LayoutInProgress();
+ if (model_->safety_state() == DownloadItem::DANGEROUS)
+ LayoutPromptDangerousDownload();
+ else
+ LayoutInProgress();
break;
case DownloadItem::REMOVING:
break;
@@ -238,6 +276,11 @@ void DownloadItemTabView::LayoutComplete() {
cancel_->SetEnabled(false);
time_remaining_->SetVisible(false);
download_progress_->SetVisible(false);
+ dangerous_download_warning_->SetVisible(false);
+ save_button_->SetVisible(false);
+ save_button_->SetEnabled(false);
+ discard_button_->SetVisible(false);
+ discard_button_->SetEnabled(false);
LayoutDate();
int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset +
@@ -286,6 +329,11 @@ void DownloadItemTabView::LayoutCancelled() {
pause_->SetEnabled(false);
cancel_->SetVisible(false);
cancel_->SetEnabled(false);
+ dangerous_download_warning_->SetVisible(false);
+ save_button_->SetVisible(false);
+ save_button_->SetEnabled(false);
+ discard_button_->SetVisible(false);
+ discard_button_->SetEnabled(false);
LayoutDate();
int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset +
@@ -372,6 +420,11 @@ void DownloadItemTabView::LayoutInProgress() {
// Hide unused UI elements
show_->SetVisible(false);
show_->SetEnabled(false);
+ dangerous_download_warning_->SetVisible(false);
+ save_button_->SetVisible(false);
+ save_button_->SetEnabled(false);
+ discard_button_->SetVisible(false);
+ discard_button_->SetEnabled(false);
LayoutDate();
int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset +
@@ -380,7 +433,7 @@ void DownloadItemTabView::LayoutInProgress() {
// File name and URL, truncated to show progress status
CSize file_name_size;
- file_name_->SetText(model_->file_name());
+ file_name_->SetText(model_->GetFileName());
file_name_->GetPreferredSize(&file_name_size);
file_name_->SetBounds(dx, download_util::kBigProgressIconOffset,
kFilenameSize - kProgressSize - kSpacer,
@@ -514,7 +567,7 @@ void DownloadItemTabView::LayoutInProgress() {
pause_->GetPreferredSize(&pause_size);
pause_->SetBounds(dx, y_pos, pause_size.cx, pause_size.cy);
- dx += pause_size.cx + kHorizontalButtonPadding;
+ dx += pause_size.cx + kHorizontalLinkPadding;
CSize cancel_size;
cancel_->GetPreferredSize(&cancel_size);
@@ -523,10 +576,69 @@ void DownloadItemTabView::LayoutInProgress() {
cancel_->SetEnabled(true);
}
+void DownloadItemTabView::LayoutPromptDangerousDownload() {
+ // Hide unused UI elements
+ show_->SetVisible(false);
+ show_->SetEnabled(false);
+ file_name_->SetVisible(false);
+ file_name_->SetEnabled(false);
+ pause_->SetVisible(false);
+ pause_->SetEnabled(false);
+ cancel_->SetVisible(false);
+ cancel_->SetEnabled(false);
+ time_remaining_->SetVisible(false);
+ download_progress_->SetVisible(false);
+
+ LayoutDate();
+ int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset +
+ download_util::kBigProgressIconSize +
+ kInfoPadding;
+
+ // Warning message and URL.
+ CSize warning_size;
+ std::wstring file_name;
+ ElideString(model_->original_name(), kFileNameMaxLength, &file_name);
+ dangerous_download_warning_->SetText(
+ l10n_util::GetStringF(IDS_PROMPT_DANGEROUS_DOWNLOAD, file_name));
+ dangerous_download_warning_->GetPreferredSize(&warning_size);
+ dangerous_download_warning_->SetBounds(dx, 0,
+ kFilenameSize, warning_size.cy);
+ dangerous_download_warning_->SetVisible(true);
+
+ GURL url(model_->url());
+ download_url_->SetURL(url);
+ CSize url_size;
+ download_url_->GetPreferredSize(&url_size);
+ download_url_->SetBounds(dx, height() - url_size.cy,
+ std::min(kFilenameSize - kSpacer,
+ static_cast<int>(width() - dx)),
+ url_size.cy);
+ download_url_->SetVisible(true);
+
+ dx += kFilenameSize + kSpacer;
+
+ // Save/Discard buttons.
+ CSize button_size;
+ save_button_->GetPreferredSize(&button_size);
+ save_button_->SetBounds(dx, (height() - button_size.cy) / 2,
+ button_size.cx, button_size.cy);
+ save_button_->SetVisible(true);
+ save_button_->SetEnabled(true);
+
+ dx += button_size.cx + kHorizontalButtonPadding;
+
+ discard_button_->GetPreferredSize(&button_size);
+ discard_button_->SetBounds(dx, (height() - button_size.cy) / 2,
+ button_size.cx, button_size.cy);
+ discard_button_->SetVisible(true);
+ discard_button_->SetEnabled(true);
+}
+
void DownloadItemTabView::Paint(ChromeCanvas* canvas) {
PaintBackground(canvas);
- if (model_->state() == DownloadItem::IN_PROGRESS) {
+ if (model_->state() == DownloadItem::IN_PROGRESS &&
+ model_->safety_state() != DownloadItem::DANGEROUS) {
download_util::PaintDownloadProgress(canvas,
this,
kDownloadIconOffset -
@@ -605,7 +717,10 @@ bool DownloadItemTabView::OnMousePressed(const ChromeViews::MouseEvent& event) {
if (select_rect.PtInRect(point)) {
parent_->ItemBecameSelected(model_);
- if (event.IsRightMouseButton()) {
+ // Don't show the right-click menu if we are prompting the user for a
+ // dangerous download.
+ if (event.IsRightMouseButton() &&
+ model_->safety_state() != DownloadItem::DANGEROUS) {
ChromeViews::View::ConvertPointToScreen(this, &point);
download_util::DownloadDestinationContextMenu menu(
@@ -620,7 +735,8 @@ bool DownloadItemTabView::OnMousePressed(const ChromeViews::MouseEvent& event) {
// Handle drag (file copy) operations.
bool DownloadItemTabView::OnMouseDragged(const ChromeViews::MouseEvent& event) {
- if (model_->state() != DownloadItem::COMPLETE)
+ if (model_->state() != DownloadItem::COMPLETE ||
+ model_->safety_state() == DownloadItem::DANGEROUS)
return false;
CPoint point(event.x(), event.y());
@@ -665,6 +781,18 @@ void DownloadItemTabView::LinkActivated(ChromeViews::Link* source,
parent_->ItemBecameSelected(model_);
}
+void DownloadItemTabView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == save_button_) {
+ parent_->model()->DangerousDownloadValidated(model_);
+ // Relayout and repaint to display the right mode (complete or in progress).
+ Layout();
+ SchedulePaint();
+ } else if (sender == discard_button_) {
+ model_->Remove(true);
+ } else {
+ NOTREACHED();
+ }
+}
// DownloadTabView implementation ----------------------------------------------
@@ -683,6 +811,7 @@ DownloadTabView::~DownloadTabView() {
// DownloadManager owns the contents.
downloads_.clear();
ClearDownloadInProgress();
+ ClearDangerousDownloads();
icon_consumer_.CancelAllRequests();
}
@@ -720,6 +849,21 @@ void DownloadTabView::DidChangeBounds(const CRect& previous,
void DownloadTabView::Layout() {
CRect r;
DetachAllFloatingViews();
+ // Dangerous downloads items use NativeButtons, so they need to be attached
+ // as NativeControls are not supported yet in floating views.
+ gfx::Rect visible_bounds = GetVisibleBounds();
+ int row_start = (visible_bounds.y() - kSpacer) /
+ (download_util::kBigProgressIconSize + kSpacer);
+ int row_stop = (visible_bounds.y() - kSpacer + visible_bounds.height()) /
+ (download_util::kBigProgressIconSize + kSpacer);
+ row_stop = std::min(row_stop, static_cast<int>(downloads_.size()) - 1);
+ for (int i = row_start; i <= row_stop; ++i) {
+ // The DownloadManager stores downloads earliest first, but this view
+ // displays latest first, so adjust the index:
+ int index = static_cast<int>(downloads_.size()) - 1 - i;
+ if (downloads_[index]->safety_state() == DownloadItem::DANGEROUS)
+ ValidateFloatingViewForID(index);
+ }
View* v = GetParent();
if (v) {
v->GetLocalBounds(&r, true);
@@ -790,12 +934,15 @@ ChromeViews::View* DownloadTabView::CreateFloatingViewForIndex(int index) {
}
DownloadItemTabView* dl = new DownloadItemTabView();
+ // We attach the view before layout as the Save/Discard buttons are native
+ // and need to be in the tree hierarchy to compute their preferred size
+ // correctly.
+ AttachFloatingView(dl, index);
dl->SetModel(downloads_[index], this);
int row = static_cast<int>(downloads_.size()) - 1 - index;
int y_pos = row * (download_util::kBigProgressIconSize + kSpacer) + kSpacer;
dl->SetBounds(0, y_pos, width(), download_util::kBigProgressIconSize);
dl->Layout();
- AttachFloatingView(dl, index);
return dl;
}
@@ -817,7 +964,10 @@ void DownloadTabView::OnDownloadUpdated(DownloadItem* download) {
case DownloadItem::CANCELLED: {
base::hash_set<DownloadItem*>::iterator d = in_progress_.find(download);
if (d != in_progress_.end()) {
- (*d)->RemoveObserver(this);
+ // If this is a dangerous download not yet validated by the user, we
+ // still need to be notified when the validation happens.
+ if (download->safety_state() != DownloadItem::DANGEROUS)
+ (*d)->RemoveObserver(this);
in_progress_.erase(d);
}
if (in_progress_.empty())
@@ -877,6 +1027,7 @@ void DownloadTabView::OnDownloadUpdated(DownloadItem* download) {
void DownloadTabView::ModelChanged() {
downloads_.clear();
ClearDownloadInProgress();
+ ClearDangerousDownloads();
DetachAllFloatingViews();
// Issue the query.
@@ -890,6 +1041,7 @@ void DownloadTabView::SetDownloads(std::vector<DownloadItem*>& downloads) {
// Clear out old state and remove self as observer for each download.
downloads_.clear();
ClearDownloadInProgress();
+ ClearDangerousDownloads();
// Swap new downloads in.
downloads_.swap(downloads);
@@ -902,6 +1054,10 @@ void DownloadTabView::SetDownloads(std::vector<DownloadItem*>& downloads) {
if (download->state() == DownloadItem::IN_PROGRESS) {
download->AddObserver(this);
in_progress_.insert(download);
+ } else if (download->safety_state() == DownloadItem::DANGEROUS) {
+ // We need to be notified when the user validates the dangerous download.
+ download->AddObserver(this);
+ dangerous_downloads_.insert(download);
}
}
@@ -949,6 +1105,14 @@ void DownloadTabView::ClearDownloadInProgress() {
in_progress_.clear();
}
+void DownloadTabView::ClearDangerousDownloads() {
+ base::hash_set<DownloadItem*>::const_iterator it;
+ for (it = dangerous_downloads_.begin();
+ it != dangerous_downloads_.end(); ++it)
+ (*it)->RemoveObserver(this);
+ dangerous_downloads_.clear();
+}
+
// Check to see if the download is the latest download on a given day.
// We use this to determine when to draw the date next to a particular
// download view: if the DownloadItem is the latest download on a given
diff --git a/chrome/browser/views/download_tab_view.h b/chrome/browser/views/download_tab_view.h
index 5c1b2ec..f78ee4a 100644
--- a/chrome/browser/views/download_tab_view.h
+++ b/chrome/browser/views/download_tab_view.h
@@ -25,7 +25,8 @@ class Timer;
}
class DownloadItemTabView : public ChromeViews::View,
- public ChromeViews::LinkController {
+ public ChromeViews::LinkController,
+ public ChromeViews::NativeButton::Listener {
public:
DownloadItemTabView();
virtual ~DownloadItemTabView();
@@ -44,10 +45,14 @@ class DownloadItemTabView : public ChromeViews::View,
void LayoutComplete();
void LayoutCancelled();
void LayoutInProgress();
+ void LayoutPromptDangerousDownload();
// LinkController overrides
virtual void LinkActivated(ChromeViews::Link* source, int event_flags);
+ // NativeButton Listener overrides.
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
// Used to set our model temporarily during layout and paint operations
void SetModel(DownloadItem* model, DownloadTabView* parent);
@@ -58,6 +63,9 @@ private:
// Containing view.
DownloadTabView* parent_;
+ // Whether we are the renderer for floating views.
+ bool is_floating_view_renderer_;
+
// Time display.
ChromeViews::Label* since_;
ChromeViews::Label* date_;
@@ -72,11 +80,19 @@ private:
ChromeViews::Label* time_remaining_;
ChromeViews::Label* download_progress_;
+ // The message warning of a dangerous download.
+ ChromeViews::Label* dangerous_download_warning_;
+
// Actions that can be initiated.
ChromeViews::Link* pause_;
ChromeViews::Link* cancel_;
ChromeViews::Link* show_;
+ // The buttons used to prompt the user when a dangerous download has been
+ // initiated.
+ ChromeViews::NativeButton* save_button_;
+ ChromeViews::NativeButton* discard_button_;
+
DISALLOW_EVIL_CONSTRUCTORS(DownloadItemTabView);
};
@@ -154,10 +170,14 @@ class DownloadTabView : public ChromeViews::View,
// Initiates an asynchronous icon extraction.
void LoadIcon(DownloadItem* download);
- // Clears the list of "in progress" downloads and removes the this
- // DownloadTabView from their observer list.
+ // Clears the list of "in progress" downloads and removes this DownloadTabView
+ // from their observer list.
void ClearDownloadInProgress();
+ // Clears the list of dangerous downloads and removes this DownloadTabView
+ // from their observer list.
+ void ClearDangerousDownloads();
+
// Our model
DownloadManager* model_;
@@ -178,6 +198,10 @@ class DownloadTabView : public ChromeViews::View,
// does not own the DownloadItems.
base::hash_set<DownloadItem*> in_progress_;
+ // Keeps track of the downloads we are an observer for as a consequence of
+ // being a dangerous download.
+ base::hash_set<DownloadItem*> dangerous_downloads_;
+
// Provide a start position for downloads with no known size.
int start_angle_;