This source file includes following definitions.
- weak_ptr_factory_
- UpdateDownloadProgress
- StartDownloadProgress
- StopDownloadProgress
- OnExtractIconComplete
- OnDownloadUpdated
- OnDownloadDestroyed
- OnDownloadOpened
- Layout
- GetPreferredSize
- OnMousePressed
- OnMouseDragged
- OnMouseReleased
- OnMouseCaptureLost
- OnMouseMoved
- OnMouseExited
- OnKeyPressed
- GetTooltipText
- GetAccessibleState
- OnThemeChanged
- OnGestureEvent
- ShowContextMenuForView
- ButtonPressed
- AnimationProgressed
- OnPaint
- OnPaintBackground
- OnFocus
- OnBlur
- OpenDownload
- SubmitDownloadToFeedbackService
- PossiblySubmitDownloadToFeedbackService
- LoadIcon
- LoadIconIfItemPathChanged
- UpdateColorsFromTheme
- ShowContextMenuImpl
- HandlePressEvent
- HandleClickEvent
- PaintImages
- SetState
- ClearWarningDialog
- ShowWarningDialog
- GetButtonSize
- SizeLabelToMinWidth
- Reenable
- ReleaseDropDown
- InDropDownButtonXCoordinateRange
- UpdateAccessibleName
- UpdateDropDownButtonPosition
- AnimateStateTransition
#include "chrome/browser/ui/views/download/download_item_view.h"
#include <algorithm>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/i18n/break_iterator.h"
#include "base/i18n/rtl.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_stats.h"
#include "chrome/browser/download/drag_download_item.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/download_feedback_service.h"
#include "chrome/browser/safe_browsing/download_protection_service.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/views/download/download_feedback_dialog_view.h"
#include "chrome/browser/ui/views/download/download_shelf_context_menu_view.h"
#include "chrome/browser/ui/views/download/download_shelf_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "content/public/browser/download_danger_type.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "third_party/icu/source/common/unicode/uchar.h"
#include "ui/accessibility/ax_view_state.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"
#include "ui/events/event.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/text_elider.h"
#include "ui/gfx/text_utils.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/mouse_constants.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"
static const int kTextWidth = 140;
static const int kDangerousTextWidth = 200;
static const int kVerticalPadding = 3;
static const int kVerticalTextPadding = 2;
static const int kTooltipMaxWidth = 800;
static const int kLeftPadding = 0;
static const int kButtonPadding = 5;
static const int kLabelPadding = 4;
static const SkColor kFileNameDisabledColor = SkColorSetRGB(171, 192, 212);
static const int kCompleteAnimationDurationMs = 2500;
static const int kInterruptedAnimationDurationMs = 2500;
static const int kDisabledOnOpenDuration = 3000;
static const double kDownloadItemLuminanceMod = 0.8;
using content::DownloadItem;
DownloadItemView::DownloadItemView(DownloadItem* download_item,
DownloadShelfView* parent)
: warning_icon_(NULL),
shelf_(parent),
status_text_(l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING)),
body_state_(NORMAL),
drop_down_state_(NORMAL),
mode_(NORMAL_MODE),
progress_angle_(DownloadShelf::kStartAngleDegrees),
drop_down_pressed_(false),
dragging_(false),
starting_drag_(false),
model_(download_item),
save_button_(NULL),
discard_button_(NULL),
dangerous_download_label_(NULL),
dangerous_download_label_sized_(false),
disabled_while_opening_(false),
creation_time_(base::Time::Now()),
time_download_warning_shown_(base::Time()),
weak_ptr_factory_(this) {
DCHECK(download());
download()->AddObserver(this);
set_context_menu_controller(this);
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
BodyImageSet normal_body_image_set = {
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_TOP),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_TOP),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_TOP),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM)
};
normal_body_image_set_ = normal_body_image_set;
DropDownImageSet normal_drop_down_image_set = {
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_MENU_TOP),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_MENU_MIDDLE),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_MENU_BOTTOM)
};
normal_drop_down_image_set_ = normal_drop_down_image_set;
BodyImageSet hot_body_image_set = {
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_TOP_H),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_H),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_H),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_TOP_H),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_H),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_H),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_TOP_H),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_H),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_H)
};
hot_body_image_set_ = hot_body_image_set;
DropDownImageSet hot_drop_down_image_set = {
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_MENU_TOP_H),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_H),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_H)
};
hot_drop_down_image_set_ = hot_drop_down_image_set;
BodyImageSet pushed_body_image_set = {
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_TOP_P),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_P),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_P),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_TOP_P),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_P),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_P),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_TOP_P),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_P),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_P)
};
pushed_body_image_set_ = pushed_body_image_set;
DropDownImageSet pushed_drop_down_image_set = {
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_MENU_TOP_P),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_P),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_P)
};
pushed_drop_down_image_set_ = pushed_drop_down_image_set;
BodyImageSet dangerous_mode_body_image_set = {
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_TOP),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_TOP),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_TOP_NO_DD),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_NO_DD),
rb.GetImageSkiaNamed(IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_NO_DD)
};
dangerous_mode_body_image_set_ = dangerous_mode_body_image_set;
malicious_mode_body_image_set_ = normal_body_image_set;
LoadIcon();
font_list_ = rb.GetFontList(ui::ResourceBundle::BaseFont);
box_height_ = std::max<int>(2 * kVerticalPadding + font_list_.GetHeight() +
kVerticalTextPadding + font_list_.GetHeight(),
2 * kVerticalPadding +
normal_body_image_set_.top_left->height() +
normal_body_image_set_.bottom_left->height());
if (DownloadShelf::kSmallProgressIconSize > box_height_)
box_y_ = (DownloadShelf::kSmallProgressIconSize - box_height_) / 2;
else
box_y_ = 0;
body_hover_animation_.reset(new gfx::SlideAnimation(this));
drop_hover_animation_.reset(new gfx::SlideAnimation(this));
SetAccessibilityFocusable(true);
OnDownloadUpdated(download());
UpdateDropDownButtonPosition();
}
DownloadItemView::~DownloadItemView() {
StopDownloadProgress();
download()->RemoveObserver(this);
}
void DownloadItemView::UpdateDownloadProgress() {
progress_angle_ =
(progress_angle_ + DownloadShelf::kUnknownIncrementDegrees) %
DownloadShelf::kMaxDegrees;
SchedulePaint();
}
void DownloadItemView::StartDownloadProgress() {
if (progress_timer_.IsRunning())
return;
progress_timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(DownloadShelf::kProgressRateMs), this,
&DownloadItemView::UpdateDownloadProgress);
}
void DownloadItemView::StopDownloadProgress() {
progress_timer_.Stop();
}
void DownloadItemView::OnExtractIconComplete(gfx::Image* icon_bitmap) {
if (icon_bitmap)
shelf_->SchedulePaint();
}
void DownloadItemView::OnDownloadUpdated(DownloadItem* download_item) {
DCHECK_EQ(download(), download_item);
if (IsShowingWarningDialog() && !model_.IsDangerous()) {
ClearWarningDialog();
} else if (!IsShowingWarningDialog() && model_.IsDangerous()) {
ShowWarningDialog();
shelf_->Layout();
SchedulePaint();
} else {
base::string16 status_text = model_.GetStatusText();
switch (download()->GetState()) {
case DownloadItem::IN_PROGRESS:
download()->IsPaused() ?
StopDownloadProgress() : StartDownloadProgress();
LoadIconIfItemPathChanged();
break;
case DownloadItem::INTERRUPTED:
StopDownloadProgress();
complete_animation_.reset(new gfx::SlideAnimation(this));
complete_animation_->SetSlideDuration(kInterruptedAnimationDurationMs);
complete_animation_->SetTweenType(gfx::Tween::LINEAR);
complete_animation_->Show();
SchedulePaint();
LoadIcon();
break;
case DownloadItem::COMPLETE:
if (model_.ShouldRemoveFromShelfWhenComplete()) {
shelf_->RemoveDownloadView(this);
return;
}
StopDownloadProgress();
complete_animation_.reset(new gfx::SlideAnimation(this));
complete_animation_->SetSlideDuration(kCompleteAnimationDurationMs);
complete_animation_->SetTweenType(gfx::Tween::LINEAR);
complete_animation_->Show();
SchedulePaint();
LoadIcon();
break;
case DownloadItem::CANCELLED:
StopDownloadProgress();
if (complete_animation_)
complete_animation_->Stop();
LoadIcon();
break;
default:
NOTREACHED();
}
status_text_ = status_text;
}
base::string16 new_tip = model_.GetTooltipText(font_list_, kTooltipMaxWidth);
if (new_tip != tooltip_text_) {
tooltip_text_ = new_tip;
TooltipTextChanged();
}
UpdateAccessibleName();
shelf_->SchedulePaint();
}
void DownloadItemView::OnDownloadDestroyed(DownloadItem* download) {
shelf_->RemoveDownloadView(this);
}
void DownloadItemView::OnDownloadOpened(DownloadItem* download) {
disabled_while_opening_ = true;
SetEnabled(false);
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&DownloadItemView::Reenable, weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kDisabledOnOpenDuration));
shelf_->OpenedDownload(this);
}
void DownloadItemView::Layout() {
if (IsShowingWarningDialog()) {
BodyImageSet* body_image_set =
(mode_ == DANGEROUS_MODE) ? &dangerous_mode_body_image_set_ :
&malicious_mode_body_image_set_;
int x = kLeftPadding + 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());
gfx::Size button_size = GetButtonSize();
x += dangerous_download_label_->width() + kLabelPadding;
y = (height() - button_size.height()) / 2;
if (save_button_) {
save_button_->SetBounds(x, y, button_size.width(), button_size.height());
x += button_size.width() + kButtonPadding;
}
discard_button_->SetBounds(x, y, button_size.width(), button_size.height());
UpdateColorsFromTheme();
}
}
gfx::Size DownloadItemView::GetPreferredSize() {
int width, height;
height = 2 * kVerticalPadding + 2 * font_list_.GetHeight() +
kVerticalTextPadding;
height = std::max<int>(height, DownloadShelf::kSmallProgressIconSize);
if (IsShowingWarningDialog()) {
BodyImageSet* body_image_set =
(mode_ == DANGEROUS_MODE) ? &dangerous_mode_body_image_set_ :
&malicious_mode_body_image_set_;
width = kLeftPadding + body_image_set->top_left->width();
width += warning_icon_->width() + kLabelPadding;
width += dangerous_download_label_->width() + kLabelPadding;
gfx::Size button_size = GetButtonSize();
height = std::max<int>(height, 2 * kVerticalPadding + button_size.height());
height = std::max<int>(height, 2 * kVerticalPadding +
warning_icon_->height());
if (save_button_)
width += button_size.width() + kButtonPadding;
width += button_size.width();
width += body_image_set->top_right->width();
if (mode_ == MALICIOUS_MODE)
width += normal_drop_down_image_set_.top->width();
} else {
width = kLeftPadding + normal_body_image_set_.top_left->width();
width += DownloadShelf::kSmallProgressIconSize;
width += kTextWidth;
width += normal_body_image_set_.top_right->width();
width += normal_drop_down_image_set_.top->width();
}
return gfx::Size(width, height);
}
bool DownloadItemView::OnMousePressed(const ui::MouseEvent& event) {
HandlePressEvent(event, event.IsOnlyLeftMouseButton());
return true;
}
bool DownloadItemView::OnMouseDragged(const ui::MouseEvent& event) {
if (IsShowingWarningDialog())
return true;
if (!starting_drag_) {
starting_drag_ = true;
drag_start_point_ = event.location();
}
if (dragging_) {
if (download()->GetState() == DownloadItem::COMPLETE) {
IconManager* im = g_browser_process->icon_manager();
gfx::Image* icon = im->LookupIconFromFilepath(
download()->GetTargetFilePath(), IconLoader::SMALL);
views::Widget* widget = GetWidget();
DragDownloadItem(
download(), icon, widget ? widget->GetNativeView() : NULL);
}
} else if (ExceededDragThreshold(event.location() - drag_start_point_)) {
dragging_ = true;
}
return true;
}
void DownloadItemView::OnMouseReleased(const ui::MouseEvent& event) {
HandleClickEvent(event, event.IsOnlyLeftMouseButton());
}
void DownloadItemView::OnMouseCaptureLost() {
if (mode_ == DANGEROUS_MODE)
return;
if (dragging_) {
dragging_ = false;
starting_drag_ = false;
}
SetState(NORMAL, NORMAL);
}
void DownloadItemView::OnMouseMoved(const ui::MouseEvent& event) {
if (mode_ == DANGEROUS_MODE)
return;
bool on_body = !InDropDownButtonXCoordinateRange(event.x());
SetState(on_body ? HOT : NORMAL, on_body ? NORMAL : HOT);
}
void DownloadItemView::OnMouseExited(const ui::MouseEvent& event) {
if (mode_ == DANGEROUS_MODE)
return;
SetState(NORMAL, drop_down_pressed_ ? PUSHED : NORMAL);
}
bool DownloadItemView::OnKeyPressed(const ui::KeyEvent& event) {
if (IsShowingWarningDialog())
return true;
if (event.key_code() == ui::VKEY_SPACE ||
event.key_code() == ui::VKEY_RETURN) {
OpenDownload();
return true;
}
return false;
}
bool DownloadItemView::GetTooltipText(const gfx::Point& p,
base::string16* tooltip) const {
if (IsShowingWarningDialog()) {
tooltip->clear();
return false;
}
tooltip->assign(tooltip_text_);
return true;
}
void DownloadItemView::GetAccessibleState(ui::AXViewState* state) {
state->name = accessible_name_;
state->role = ui::AX_ROLE_BUTTON;
if (model_.IsDangerous())
state->AddStateFlag(ui::AX_STATE_DISABLED);
else
state->AddStateFlag(ui::AX_STATE_HASPOPUP);
}
void DownloadItemView::OnThemeChanged() {
UpdateColorsFromTheme();
}
void DownloadItemView::OnGestureEvent(ui::GestureEvent* event) {
if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
HandlePressEvent(*event, true);
event->SetHandled();
return;
}
if (event->type() == ui::ET_GESTURE_TAP) {
HandleClickEvent(*event, true);
event->SetHandled();
return;
}
SetState(NORMAL, NORMAL);
views::View::OnGestureEvent(event);
}
void DownloadItemView::ShowContextMenuForView(View* source,
const gfx::Point& point,
ui::MenuSourceType source_type) {
gfx::Point local_point = point;
ConvertPointFromScreen(this, &local_point);
ShowContextMenuImpl(local_point, source_type);
}
void DownloadItemView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
base::TimeDelta warning_duration;
if (!time_download_warning_shown_.is_null())
warning_duration = base::Time::Now() - time_download_warning_shown_;
if (save_button_ && sender == save_button_) {
UMA_HISTOGRAM_LONG_TIMES("clickjacking.save_download", warning_duration);
download()->ValidateDangerousDownload();
return;
}
DCHECK_EQ(discard_button_, sender);
if (model_.IsMalicious()) {
UMA_HISTOGRAM_LONG_TIMES("clickjacking.dismiss_download", warning_duration);
shelf_->RemoveDownloadView(this);
return;
}
UMA_HISTOGRAM_LONG_TIMES("clickjacking.discard_download", warning_duration);
if (model_.ShouldAllowDownloadFeedback() &&
!shelf_->browser()->profile()->IsOffTheRecord()) {
if (!shelf_->browser()->profile()->GetPrefs()->HasPrefPath(
prefs::kSafeBrowsingDownloadFeedbackEnabled)) {
DownloadFeedbackDialogView::Show(
shelf_->get_parent()->GetNativeWindow(),
shelf_->browser()->profile(),
base::Bind(
&DownloadItemView::PossiblySubmitDownloadToFeedbackService,
weak_ptr_factory_.GetWeakPtr()));
} else {
PossiblySubmitDownloadToFeedbackService(
shelf_->browser()->profile()->GetPrefs()->GetBoolean(
prefs::kSafeBrowsingDownloadFeedbackEnabled));
}
return;
}
download()->Remove();
}
void DownloadItemView::AnimationProgressed(const gfx::Animation* animation) {
SchedulePaint();
}
void DownloadItemView::OnPaint(gfx::Canvas* canvas) {
OnPaintBackground(canvas);
if (HasFocus())
canvas->DrawFocusRect(GetLocalBounds());
}
void DownloadItemView::OnPaintBackground(gfx::Canvas* canvas) {
BodyImageSet* body_image_set = NULL;
switch (mode_) {
case NORMAL_MODE:
if (body_state_ == PUSHED)
body_image_set = &pushed_body_image_set_;
else
body_image_set = &normal_body_image_set_;
break;
case DANGEROUS_MODE:
body_image_set = &dangerous_mode_body_image_set_;
break;
case MALICIOUS_MODE:
body_image_set = &malicious_mode_body_image_set_;
break;
default:
NOTREACHED();
}
DropDownImageSet* drop_down_image_set = NULL;
switch (mode_) {
case NORMAL_MODE:
case MALICIOUS_MODE:
if (drop_down_state_ == PUSHED)
drop_down_image_set = &pushed_drop_down_image_set_;
else
drop_down_image_set = &normal_drop_down_image_set_;
break;
case 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);
if (center_width <= 0)
return;
if (!IsShowingWarningDialog()) {
if (!status_text_.empty()) {
int mirrored_x = GetMirroredXWithWidthInView(
DownloadShelf::kSmallProgressIconSize, kTextWidth);
int y = box_y_ + kVerticalPadding + font_list_.GetHeight() +
kVerticalTextPadding;
SkColor file_name_color = GetThemeProvider()->GetColor(
ThemeProperties::COLOR_BOOKMARK_TEXT);
if (color_utils::RelativeLuminance(file_name_color) > 0.5)
file_name_color = SkColorSetRGB(
static_cast<int>(kDownloadItemLuminanceMod *
SkColorGetR(file_name_color)),
static_cast<int>(kDownloadItemLuminanceMod *
SkColorGetG(file_name_color)),
static_cast<int>(kDownloadItemLuminanceMod *
SkColorGetB(file_name_color)));
canvas->DrawStringRect(status_text_, font_list_, file_name_color,
gfx::Rect(mirrored_x, y, kTextWidth,
font_list_.GetHeight()));
}
}
int x = kLeftPadding;
canvas->Save();
if (base::i18n::IsRTL()) {
canvas->Translate(gfx::Vector2d(width(), 0));
canvas->Scale(-1, 1);
}
PaintImages(canvas,
body_image_set->top_left, body_image_set->left,
body_image_set->bottom_left,
x, box_y_, box_height_, body_image_set->top_left->width());
x += body_image_set->top_left->width();
PaintImages(canvas,
body_image_set->top, body_image_set->center,
body_image_set->bottom,
x, box_y_, box_height_, center_width);
x += center_width;
PaintImages(canvas,
body_image_set->top_right, body_image_set->right,
body_image_set->bottom_right,
x, box_y_, box_height_, body_image_set->top_right->width());
if (!IsShowingWarningDialog() &&
body_hover_animation_->GetCurrentValue() > 0) {
canvas->SaveLayerAlpha(
static_cast<int>(body_hover_animation_->GetCurrentValue() * 255));
int x = kLeftPadding;
PaintImages(canvas,
hot_body_image_set_.top_left, hot_body_image_set_.left,
hot_body_image_set_.bottom_left,
x, box_y_, box_height_, hot_body_image_set_.top_left->width());
x += body_image_set->top_left->width();
PaintImages(canvas,
hot_body_image_set_.top, hot_body_image_set_.center,
hot_body_image_set_.bottom,
x, box_y_, box_height_, center_width);
x += center_width;
PaintImages(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());
canvas->Restore();
}
x += body_image_set->top_right->width();
if (drop_down_image_set) {
PaintImages(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());
if (drop_hover_animation_->GetCurrentValue() > 0) {
canvas->SaveLayerAlpha(
static_cast<int>(drop_hover_animation_->GetCurrentValue() * 255));
PaintImages(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();
}
}
canvas->Restore();
if (!IsShowingWarningDialog()) {
base::string16 filename;
if (!disabled_while_opening_) {
filename = gfx::ElideFilename(download()->GetFileNameToReportUser(),
font_list_, kTextWidth);
} else {
base::string16 status_string =
l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_OPENING,
base::string16());
int status_string_width = gfx::GetStringWidth(status_string, font_list_);
base::string16 filename_string =
gfx::ElideFilename(download()->GetFileNameToReportUser(), font_list_,
kTextWidth - status_string_width);
filename = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_OPENING,
filename_string);
}
int mirrored_x = GetMirroredXWithWidthInView(
DownloadShelf::kSmallProgressIconSize, kTextWidth);
SkColor file_name_color = GetThemeProvider()->GetColor(
ThemeProperties::COLOR_BOOKMARK_TEXT);
int y =
box_y_ + (status_text_.empty() ?
((box_height_ - font_list_.GetHeight()) / 2) : kVerticalPadding);
canvas->DrawStringRect(
filename, font_list_,
enabled() ? file_name_color : kFileNameDisabledColor,
gfx::Rect(mirrored_x, y, kTextWidth, font_list_.GetHeight()));
}
IconManager* im = g_browser_process->icon_manager();
gfx::Image* image = im->LookupIconFromFilepath(
download()->GetTargetFilePath(), IconLoader::SMALL);
const gfx::ImageSkia* icon = NULL;
if (IsShowingWarningDialog())
icon = warning_icon_;
else if (image)
icon = image->ToImageSkia();
if (icon) {
if (!IsShowingWarningDialog()) {
DownloadItem::DownloadState state = download()->GetState();
if (state == DownloadItem::IN_PROGRESS) {
DownloadShelf::PaintDownloadProgress(canvas,
this,
0,
0,
progress_angle_,
model_.PercentComplete(),
DownloadShelf::SMALL);
} else if (complete_animation_.get() &&
complete_animation_->is_animating()) {
if (state == DownloadItem::INTERRUPTED) {
DownloadShelf::PaintDownloadInterrupted(
canvas,
this,
0,
0,
complete_animation_->GetCurrentValue(),
DownloadShelf::SMALL);
} else {
DCHECK_EQ(DownloadItem::COMPLETE, state);
DownloadShelf::PaintDownloadComplete(
canvas,
this,
0,
0,
complete_animation_->GetCurrentValue(),
DownloadShelf::SMALL);
}
}
}
int icon_x, icon_y;
if (IsShowingWarningDialog()) {
icon_x = kLeftPadding + body_image_set->top_left->width();
icon_y = (height() - icon->height()) / 2;
} else {
icon_x = DownloadShelf::kSmallProgressIconOffset;
icon_y = DownloadShelf::kSmallProgressIconOffset;
}
icon_x = GetMirroredXWithWidthInView(icon_x, icon->width());
if (enabled()) {
canvas->DrawImageInt(*icon, icon_x, icon_y);
} else {
SkPaint paint;
paint.setAlpha(120);
canvas->DrawImageInt(*icon, icon_x, icon_y, paint);
}
}
}
void DownloadItemView::OnFocus() {
View::OnFocus();
SchedulePaint();
}
void DownloadItemView::OnBlur() {
View::OnBlur();
SchedulePaint();
}
void DownloadItemView::OpenDownload() {
DCHECK(!IsShowingWarningDialog());
UMA_HISTOGRAM_LONG_TIMES("clickjacking.open_download",
base::Time::Now() - creation_time_);
UpdateAccessibleName();
download()->OpenDownload();
}
bool DownloadItemView::SubmitDownloadToFeedbackService() {
#if defined(FULL_SAFE_BROWSING)
SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service();
if (!sb_service)
return false;
safe_browsing::DownloadProtectionService* download_protection_service =
sb_service->download_protection_service();
if (!download_protection_service)
return false;
download_protection_service->feedback_service()->BeginFeedbackForDownload(
download());
return true;
#else
NOTREACHED();
return false;
#endif
}
void DownloadItemView::PossiblySubmitDownloadToFeedbackService(bool enabled) {
if (!enabled || !SubmitDownloadToFeedbackService())
download()->Remove();
}
void DownloadItemView::LoadIcon() {
IconManager* im = g_browser_process->icon_manager();
last_download_item_path_ = download()->GetTargetFilePath();
im->LoadIcon(last_download_item_path_,
IconLoader::SMALL,
base::Bind(&DownloadItemView::OnExtractIconComplete,
base::Unretained(this)),
&cancelable_task_tracker_);
}
void DownloadItemView::LoadIconIfItemPathChanged() {
base::FilePath current_download_path = download()->GetTargetFilePath();
if (last_download_item_path_ == current_download_path)
return;
LoadIcon();
}
void DownloadItemView::UpdateColorsFromTheme() {
if (dangerous_download_label_ && GetThemeProvider()) {
dangerous_download_label_->SetEnabledColor(
GetThemeProvider()->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT));
}
}
void DownloadItemView::ShowContextMenuImpl(const gfx::Point& p,
ui::MenuSourceType source_type) {
gfx::Point point = p;
gfx::Size size;
static_cast<views::internal::RootView*>(GetWidget()->GetRootView())->
SetMouseHandler(NULL);
if (source_type != ui::MENU_SOURCE_MOUSE &&
source_type != ui::MENU_SOURCE_TOUCH) {
drop_down_pressed_ = true;
SetState(NORMAL, PUSHED);
point.SetPoint(drop_down_x_left_, box_y_);
size.SetSize(drop_down_x_right_ - drop_down_x_left_, box_height_);
}
base::MessageLoop::current()->PostNonNestableTask(
FROM_HERE,
base::Bind(&DownloadItemView::ReleaseDropDown,
weak_ptr_factory_.GetWeakPtr()));
views::View::ConvertPointToScreen(this, &point);
if (!context_menu_.get()) {
context_menu_.reset(
new DownloadShelfContextMenuView(download(), shelf_->GetNavigator()));
}
context_menu_->Run(GetWidget()->GetTopLevelWidget(),
gfx::Rect(point, size), source_type);
}
void DownloadItemView::HandlePressEvent(const ui::LocatedEvent& event,
bool active_event) {
if (mode_ == DANGEROUS_MODE)
return;
if (complete_animation_.get() && complete_animation_->is_animating())
complete_animation_->End();
if (active_event) {
if (InDropDownButtonXCoordinateRange(event.x())) {
if (context_menu_.get()) {
base::TimeDelta delta =
base::TimeTicks::Now() - context_menu_->close_time();
if (delta.InMilliseconds() < views::kMinimumMsBetweenButtonClicks)
return;
}
drop_down_pressed_ = true;
SetState(NORMAL, PUSHED);
ShowContextMenuImpl(event.location(), ui::MENU_SOURCE_KEYBOARD);
} else if (!IsShowingWarningDialog()) {
SetState(PUSHED, NORMAL);
}
}
}
void DownloadItemView::HandleClickEvent(const ui::LocatedEvent& event,
bool active_event) {
if (mode_ == DANGEROUS_MODE)
return;
SetState(NORMAL, NORMAL);
if (!active_event ||
InDropDownButtonXCoordinateRange(event.x()) ||
IsShowingWarningDialog()) {
return;
}
OpenDownload();
}
void DownloadItemView::PaintImages(gfx::Canvas* canvas,
const gfx::ImageSkia* top_image,
const gfx::ImageSkia* center_image,
const gfx::ImageSkia* bottom_image,
int x, int y, int height, int width) {
int middle_height = height - top_image->height() - bottom_image->height();
canvas->DrawImageInt(*top_image,
0, 0, top_image->width(), top_image->height(),
x, y, width, top_image->height(), false);
y += top_image->height();
canvas->DrawImageInt(*center_image,
0, 0, center_image->width(), center_image->height(),
x, y, width, middle_height, false);
y += middle_height;
canvas->DrawImageInt(*bottom_image,
0, 0, bottom_image->width(), bottom_image->height(),
x, y, width, bottom_image->height(), false);
}
void DownloadItemView::SetState(State new_body_state, State new_drop_state) {
if (IsShowingWarningDialog()) {
new_body_state = NORMAL;
DCHECK_EQ(NORMAL, body_state_);
DCHECK_NE(DANGEROUS_MODE, mode_);
}
if (body_state_ == new_body_state && drop_down_state_ == new_drop_state)
return;
AnimateStateTransition(body_state_, new_body_state,
body_hover_animation_.get());
AnimateStateTransition(drop_down_state_, new_drop_state,
drop_hover_animation_.get());
body_state_ = new_body_state;
drop_down_state_ = new_drop_state;
SchedulePaint();
}
void DownloadItemView::ClearWarningDialog() {
DCHECK(download()->GetDangerType() ==
content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED);
DCHECK(mode_ == DANGEROUS_MODE || mode_ == MALICIOUS_MODE);
mode_ = NORMAL_MODE;
body_state_ = NORMAL;
drop_down_state_ = NORMAL;
if (save_button_) {
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;
dangerous_download_label_sized_ = false;
cached_button_size_.SetSize(0,0);
UpdateAccessibleName();
UpdateDropDownButtonPosition();
LoadIcon();
shelf_->Layout();
shelf_->SchedulePaint();
TooltipTextChanged();
}
void DownloadItemView::ShowWarningDialog() {
DCHECK(mode_ != DANGEROUS_MODE && mode_ != MALICIOUS_MODE);
time_download_warning_shown_ = base::Time::Now();
content::DownloadDangerType danger_type = download()->GetDangerType();
RecordDangerousDownloadWarningShown(danger_type);
#if defined(FULL_SAFE_BROWSING)
if (model_.ShouldAllowDownloadFeedback()) {
safe_browsing::DownloadFeedbackService::RecordEligibleDownloadShown(
danger_type);
}
#endif
mode_ = model_.MightBeMalicious() ? MALICIOUS_MODE : DANGEROUS_MODE;
body_state_ = NORMAL;
drop_down_state_ = NORMAL;
if (mode_ == DANGEROUS_MODE) {
save_button_ = new views::LabelButton(
this, model_.GetWarningConfirmButtonText());
save_button_->SetStyle(views::Button::STYLE_BUTTON);
AddChildView(save_button_);
}
int discard_button_message = model_.IsMalicious() ?
IDS_DISMISS_DOWNLOAD : IDS_DISCARD_DOWNLOAD;
discard_button_ = new views::LabelButton(
this, l10n_util::GetStringUTF16(discard_button_message));
discard_button_->SetStyle(views::Button::STYLE_BUTTON);
AddChildView(discard_button_);
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
switch (danger_type) {
case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
warning_icon_ = rb.GetImageSkiaNamed(IDR_SAFEBROWSING_WARNING);
break;
case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
case content::DOWNLOAD_DANGER_TYPE_MAX:
NOTREACHED();
case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
warning_icon_ = rb.GetImageSkiaNamed(IDR_WARNING);
}
base::string16 dangerous_label =
model_.GetWarningText(font_list_, kTextWidth);
dangerous_download_label_ = new views::Label(dangerous_label);
dangerous_download_label_->SetMultiLine(true);
dangerous_download_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
dangerous_download_label_->SetAutoColorReadabilityEnabled(false);
AddChildView(dangerous_download_label_);
SizeLabelToMinWidth();
UpdateDropDownButtonPosition();
TooltipTextChanged();
}
gfx::Size DownloadItemView::GetButtonSize() {
DCHECK(discard_button_ && (mode_ == MALICIOUS_MODE || save_button_));
gfx::Size size;
if (cached_button_size_.width() != 0)
return cached_button_size_;
if (save_button_)
size = save_button_->GetMinimumSize();
gfx::Size discard_size = discard_button_->GetMinimumSize();
size.SetSize(std::max(size.width(), discard_size.width()),
std::max(size.height(), discard_size.height()));
if (size.width() != 0)
cached_button_size_ = size;
return size;
}
void DownloadItemView::SizeLabelToMinWidth() {
if (dangerous_download_label_sized_)
return;
base::string16 label_text = dangerous_download_label_->text();
base::TrimWhitespace(label_text, base::TRIM_ALL, &label_text);
DCHECK_EQ(base::string16::npos, label_text.find('\n'));
dangerous_download_label_->SetBounds(0, 0, 1000, 1000);
const base::string16 original_text(label_text);
base::i18n::BreakIterator iter(original_text,
base::i18n::BreakIterator::BREAK_LINE);
bool status = iter.Init();
DCHECK(status);
base::string16 prev_text = original_text;
gfx::Size size = dangerous_download_label_->GetPreferredSize();
int min_width = size.width();
while (iter.Advance() && min_width > kDangerousTextWidth) {
size_t pos = iter.pos();
if (pos >= original_text.length())
break;
base::string16 current_text = original_text;
base::char16 line_end_char = current_text[pos - 1];
if (u_isUWhiteSpace(line_end_char))
current_text.replace(pos - 1, 1, 1, base::char16('\n'));
else
current_text.insert(pos, 1, base::char16('\n'));
dangerous_download_label_->SetText(current_text);
size = dangerous_download_label_->GetPreferredSize();
if (size.width() > min_width) {
dangerous_download_label_->SetText(prev_text);
break;
} else {
min_width = size.width();
}
prev_text = current_text;
}
dangerous_download_label_->SetBounds(0, 0, size.width(), size.height());
dangerous_download_label_sized_ = true;
}
void DownloadItemView::Reenable() {
disabled_while_opening_ = false;
SetEnabled(true);
}
void DownloadItemView::ReleaseDropDown() {
drop_down_pressed_ = false;
SetState(NORMAL, NORMAL);
}
bool DownloadItemView::InDropDownButtonXCoordinateRange(int x) {
if (x > drop_down_x_left_ && x < drop_down_x_right_)
return true;
return false;
}
void DownloadItemView::UpdateAccessibleName() {
base::string16 new_name;
if (IsShowingWarningDialog()) {
new_name = dangerous_download_label_->text();
} else {
new_name = status_text_ + base::char16(' ') +
download()->GetFileNameToReportUser().LossyDisplayName();
}
if (new_name != accessible_name_) {
accessible_name_ = new_name;
NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true);
}
}
void DownloadItemView::UpdateDropDownButtonPosition() {
gfx::Size size = GetPreferredSize();
if (base::i18n::IsRTL()) {
drop_down_x_left_ = 0;
drop_down_x_right_ = normal_drop_down_image_set_.top->width();
} else {
drop_down_x_left_ =
size.width() - normal_drop_down_image_set_.top->width();
drop_down_x_right_ = size.width();
}
}
void DownloadItemView::AnimateStateTransition(State from, State to,
gfx::SlideAnimation* animation) {
if (from == NORMAL && to == HOT) {
animation->Show();
} else if (from == HOT && to == NORMAL) {
animation->Hide();
} else if (from != to) {
animation->Reset((to == HOT) ? 1.0 : 0.0);
}
}