summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views')
-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
4 files changed, 558 insertions, 78 deletions
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_;