diff options
Diffstat (limited to 'chrome/browser/views')
-rw-r--r-- | chrome/browser/views/download_item_view.cc | 369 | ||||
-rw-r--r-- | chrome/browser/views/download_item_view.h | 47 | ||||
-rw-r--r-- | chrome/browser/views/download_tab_view.cc | 190 | ||||
-rw-r--r-- | chrome/browser/views/download_tab_view.h | 30 |
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_; |