diff options
author | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-10 18:50:32 +0000 |
---|---|---|
committer | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-10 18:50:32 +0000 |
commit | 9ccbb370aa45f477941e0599d4ce7c89fac64101 (patch) | |
tree | 8b21818fa95d05ff00dfe5b9986d1d39eb4be722 /chrome/browser/views/download_item_view.cc | |
parent | a9acde54584ff37bcc5fad73cf25dce5d85348bc (diff) | |
download | chromium_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/views/download_item_view.cc')
-rw-r--r-- | chrome/browser/views/download_item_view.cc | 369 |
1 files changed, 310 insertions, 59 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 |