This source file includes following definitions.
- weak_ptr_factory_
 
- OnDownloadUpdated
 
- OnDownloadDestroyed
 
- AnimationProgressed
 
- Observe
 
- UpdateDownloadProgress
 
- StartDownloadProgress
 
- StopDownloadProgress
 
- OnLoadSmallIconComplete
 
- OnLoadLargeIconComplete
 
- LoadIcon
 
- UpdateTooltip
 
- UpdateNameLabel
 
- UpdateStatusLabel
 
- UpdateDangerWarning
 
- UpdateDangerIcon
 
- InitNineBoxes
 
- OnHboxExpose
 
- OnExpose
 
- ReenableHbox
 
- OnDownloadOpened
 
- OnClick
 
- OnButtonPress
 
- OnProgressAreaExpose
 
- OnMenuButtonPressEvent
 
- ShowPopupMenu
 
- OnDangerousPromptExpose
 
- OnDangerousAccept
 
- OnDangerousDecline
 
#include "chrome/browser/ui/gtk/download/download_item_gtk.h"
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/debug/trace_event.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.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/themes/theme_properties.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/gtk/download/download_item_drag.h"
#include "chrome/browser/ui/gtk/download/download_shelf_context_menu_gtk.h"
#include "chrome/browser/ui/gtk/download/download_shelf_gtk.h"
#include "chrome/browser/ui/gtk/gtk_theme_service.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/gtk/nine_box.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/notification_source.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/canvas_skia_paint.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/skia_utils_gtk.h"
#include "ui/gfx/text_elider.h"
#include "ui/gfx/text_utils.h"
namespace {
const int kMenuButtonWidth = 16;
const int kDangerousElementPadding = 3;
const int kDangerousTextWidth = 200;
const int kTextWidth = 140;
const int kTooltipMaxWidth = 1000;
const int kMinDownloadItemWidth = DownloadShelf::kSmallProgressIconSize;
const int kNewItemAnimationDurationMs = 800;
const int kCompleteAnimationDurationMs = 2500;
const int kBodyHeight = DownloadShelf::kSmallProgressIconSize;
const int kBodyWidth = kTextWidth + 50 + DownloadShelf::kSmallProgressIconSize;
const double kTextSize = 13.4;  
static const double kDownloadItemLuminanceMod = 0.8;
static const int kDisabledOnOpenDurationMs = 3000;
}  
NineBox* DownloadItemGtk::body_nine_box_normal_ = NULL;
NineBox* DownloadItemGtk::body_nine_box_prelight_ = NULL;
NineBox* DownloadItemGtk::body_nine_box_active_ = NULL;
NineBox* DownloadItemGtk::menu_nine_box_normal_ = NULL;
NineBox* DownloadItemGtk::menu_nine_box_prelight_ = NULL;
NineBox* DownloadItemGtk::menu_nine_box_active_ = NULL;
NineBox* DownloadItemGtk::dangerous_nine_box_ = NULL;
using content::DownloadItem;
DownloadItemGtk::DownloadItemGtk(DownloadShelfGtk* parent_shelf,
                                 DownloadItem* download_item)
    : parent_shelf_(parent_shelf),
      arrow_(NULL),
      menu_showing_(false),
      theme_service_(
          GtkThemeService::GetFrom(parent_shelf->browser()->profile())),
      progress_angle_(DownloadShelf::kStartAngleDegrees),
      download_model_(download_item),
      dangerous_prompt_(NULL),
      dangerous_label_(NULL),
      complete_animation_(this),
      icon_small_(NULL),
      icon_large_(NULL),
      creation_time_(base::Time::Now()),
      download_complete_(false),
      disabled_while_opening_(false),
      weak_ptr_factory_(this) {
  LoadIcon();
  body_.Own(gtk_button_new());
  gtk_widget_set_app_paintable(body_.get(), TRUE);
  UpdateTooltip();
  g_signal_connect(body_.get(), "expose-event",
                   G_CALLBACK(OnExposeThunk), this);
  g_signal_connect(body_.get(), "clicked",
                   G_CALLBACK(OnClickThunk), this);
  g_signal_connect(body_.get(), "button-press-event",
                   G_CALLBACK(OnButtonPressThunk), this);
  gtk_widget_set_can_focus(body_.get(), FALSE);
  
  GtkRcStyle* no_padding_style = gtk_rc_style_new();
  no_padding_style->xthickness = 0;
  no_padding_style->ythickness = 0;
  gtk_widget_modify_style(body_.get(), no_padding_style);
  g_object_unref(no_padding_style);
  name_label_ = gtk_label_new(NULL);
  
  gtk_misc_set_alignment(GTK_MISC(name_label_), 0, 0.5);
  
  gtk_util::ForceFontSizePixels(name_label_, kTextSize);
  UpdateNameLabel();
  status_label_ = NULL;
  
  text_stack_ = gtk_vbox_new(FALSE, 0);
  g_signal_connect(text_stack_, "destroy",
                   G_CALLBACK(gtk_widget_destroyed), &text_stack_);
  gtk_box_pack_start(GTK_BOX(text_stack_), name_label_, TRUE, TRUE, 0);
  
  
  progress_area_.Own(gtk_fixed_new());
  gtk_widget_set_size_request(progress_area_.get(),
      DownloadShelf::kSmallProgressIconSize,
      DownloadShelf::kSmallProgressIconSize);
  gtk_widget_set_app_paintable(progress_area_.get(), TRUE);
  g_signal_connect(progress_area_.get(), "expose-event",
                   G_CALLBACK(OnProgressAreaExposeThunk), this);
  
  GtkWidget* body_hbox = gtk_hbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(body_.get()), body_hbox);
  gtk_box_pack_start(GTK_BOX(body_hbox), progress_area_.get(), FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(body_hbox), text_stack_, TRUE, TRUE, 0);
  menu_button_ = gtk_button_new();
  gtk_widget_set_app_paintable(menu_button_, TRUE);
  gtk_widget_set_can_focus(menu_button_, FALSE);
  g_signal_connect(menu_button_, "expose-event",
                   G_CALLBACK(OnExposeThunk), this);
  g_signal_connect(menu_button_, "button-press-event",
                   G_CALLBACK(OnMenuButtonPressEventThunk), this);
  g_object_set_data(G_OBJECT(menu_button_), "left-align-popup",
                    reinterpret_cast<void*>(true));
  GtkWidget* shelf_hbox = parent_shelf->GetHBox();
  hbox_.Own(gtk_hbox_new(FALSE, 0));
  g_signal_connect(hbox_.get(), "expose-event",
                   G_CALLBACK(OnHboxExposeThunk), this);
  gtk_box_pack_start(GTK_BOX(hbox_.get()), body_.get(), FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox_.get()), menu_button_, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(shelf_hbox), hbox_.get(), FALSE, FALSE, 0);
  
  gtk_box_reorder_child(GTK_BOX(shelf_hbox), hbox_.get(), 0);
  download()->AddObserver(this);
  new_item_animation_.reset(new gfx::SlideAnimation(this));
  new_item_animation_->SetSlideDuration(kNewItemAnimationDurationMs);
  gtk_widget_show_all(hbox_.get());
  if (download_model_.IsDangerous()) {
    RecordDangerousDownloadWarningShown(download()->GetDangerType());
    
    gtk_widget_set_no_show_all(body_.get(), TRUE);
    gtk_widget_set_no_show_all(menu_button_, TRUE);
    gtk_widget_hide(body_.get());
    gtk_widget_hide(menu_button_);
    
    dangerous_hbox_.Own(gtk_hbox_new(FALSE, kDangerousElementPadding));
    
    
    GtkWidget* empty_label_a = gtk_label_new(NULL);
    GtkWidget* empty_label_b = gtk_label_new(NULL);
    gtk_box_pack_start(GTK_BOX(dangerous_hbox_.get()), empty_label_a,
                       FALSE, FALSE, 0);
    gtk_box_pack_end(GTK_BOX(dangerous_hbox_.get()), empty_label_b,
                     FALSE, FALSE, 0);
    
    dangerous_image_ = gtk_image_new();
    gtk_box_pack_start(GTK_BOX(dangerous_hbox_.get()), dangerous_image_,
                       FALSE, FALSE, 0);
    dangerous_label_ = gtk_label_new(NULL);
    
    
    gtk_box_pack_start(GTK_BOX(dangerous_hbox_.get()), dangerous_label_,
                       TRUE, TRUE, 0);
    
    GtkWidget* dangerous_decline = gtk_button_new_with_label(
        l10n_util::GetStringUTF8(IDS_DISCARD_DOWNLOAD).c_str());
    g_signal_connect(dangerous_decline, "clicked",
                     G_CALLBACK(OnDangerousDeclineThunk), this);
    gtk_util::CenterWidgetInHBox(dangerous_hbox_.get(), dangerous_decline,
                                 false, 0);
    
    if (!download_model_.IsMalicious()) {
      GtkWidget* dangerous_accept = gtk_button_new_with_label(
          base::UTF16ToUTF8(
              download_model_.GetWarningConfirmButtonText()).c_str());
      g_signal_connect(dangerous_accept, "clicked",
                       G_CALLBACK(OnDangerousAcceptThunk), this);
      gtk_util::CenterWidgetInHBox(
          dangerous_hbox_.get(), dangerous_accept, false, 0);
    }
    
    
    dangerous_prompt_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
    gtk_alignment_set_padding(GTK_ALIGNMENT(dangerous_prompt_),
        0, 0, kDangerousElementPadding, kDangerousElementPadding);
    gtk_container_add(GTK_CONTAINER(dangerous_prompt_), dangerous_hbox_.get());
    gtk_box_pack_start(GTK_BOX(hbox_.get()), dangerous_prompt_, FALSE, FALSE,
                       0);
    gtk_widget_set_app_paintable(dangerous_prompt_, TRUE);
    gtk_widget_set_redraw_on_allocate(dangerous_prompt_, TRUE);
    g_signal_connect(dangerous_prompt_, "expose-event",
                     G_CALLBACK(OnDangerousPromptExposeThunk), this);
    gtk_widget_show_all(dangerous_prompt_);
  }
  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
                 content::Source<ThemeService>(theme_service_));
  theme_service_->InitThemesFor(this);
  
  if (download_model_.IsDangerous()) {
    gtk_widget_set_size_request(dangerous_hbox_.get(),
                                dangerous_hbox_start_width_, -1);
  } else {
    gtk_widget_set_size_request(body_.get(), kMinDownloadItemWidth, -1);
  }
  new_item_animation_->Show();
  complete_animation_.SetTweenType(gfx::Tween::LINEAR);
  complete_animation_.SetSlideDuration(kCompleteAnimationDurationMs);
  
  OnDownloadUpdated(download());
}
DownloadItemGtk::~DownloadItemGtk() {
  
  if (menu_.get())
    menu_.reset();
  StopDownloadProgress();
  download()->RemoveObserver(this);
  
  parent_shelf_->MaybeShowMoreDownloadItems();
  hbox_.Destroy();
  progress_area_.Destroy();
  body_.Destroy();
  dangerous_hbox_.Destroy();
  
  
  DCHECK(!status_label_);
}
void DownloadItemGtk::OnDownloadUpdated(DownloadItem* download_item) {
  DCHECK_EQ(download(), download_item);
  if (dangerous_prompt_ != NULL && !download_model_.IsDangerous()) {
    
    gtk_widget_set_no_show_all(body_.get(), FALSE);
    gtk_widget_set_no_show_all(menu_button_, FALSE);
    gtk_widget_show_all(hbox_.get());
    gtk_widget_destroy(dangerous_prompt_);
    gtk_widget_set_size_request(body_.get(), kBodyWidth, -1);
    dangerous_prompt_ = NULL;
    
    parent_shelf_->MaybeShowMoreDownloadItems();
  }
  if (download()->GetTargetFilePath() != icon_filepath_) {
    LoadIcon();
    UpdateTooltip();
  }
  switch (download()->GetState()) {
    case DownloadItem::CANCELLED:
      StopDownloadProgress();
      gtk_widget_queue_draw(progress_area_.get());
      break;
    case DownloadItem::INTERRUPTED:
      StopDownloadProgress();
      UpdateTooltip();
      complete_animation_.Show();
      break;
    case DownloadItem::COMPLETE:
      
      
      
      if (download_model_.ShouldRemoveFromShelfWhenComplete()) {
        parent_shelf_->RemoveDownloadItem(this);  
        return;
      }
      
      
      if (download_complete_)
        break;
      StopDownloadProgress();
      
      DownloadItemDrag::SetSource(body_.get(), download(), icon_large_);
      complete_animation_.Show();
      download_complete_ = true;
      break;
    case DownloadItem::IN_PROGRESS:
      download()->IsPaused() ?
          StopDownloadProgress() : StartDownloadProgress();
      break;
    default:
      NOTREACHED();
  }
  status_text_ = base::UTF16ToUTF8(download_model_.GetStatusText());
  UpdateStatusLabel(status_text_);
}
void DownloadItemGtk::OnDownloadDestroyed(DownloadItem* download_item) {
  DCHECK_EQ(download(), download_item);
  parent_shelf_->RemoveDownloadItem(this);
  
}
void DownloadItemGtk::AnimationProgressed(const gfx::Animation* animation) {
  if (animation == &complete_animation_) {
    gtk_widget_queue_draw(progress_area_.get());
  } else {
    DCHECK(animation == new_item_animation_.get());
    if (download_model_.IsDangerous()) {
      int progress = static_cast<int>((dangerous_hbox_full_width_ -
                                       dangerous_hbox_start_width_) *
                                      animation->GetCurrentValue());
      int showing_width = dangerous_hbox_start_width_ + progress;
      gtk_widget_set_size_request(dangerous_hbox_.get(), showing_width, -1);
    } else {
      int showing_width = std::max(kMinDownloadItemWidth,
          static_cast<int>(kBodyWidth * animation->GetCurrentValue()));
      gtk_widget_set_size_request(body_.get(), showing_width, -1);
    }
  }
}
void DownloadItemGtk::Observe(int type,
                              const content::NotificationSource& source,
                              const content::NotificationDetails& details) {
  if (type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED) {
    
    
    if (theme_service_->UsingNativeTheme()) {
      if (!arrow_) {
        arrow_ = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
        gtk_widget_set_size_request(arrow_,
                                    static_cast<int>(kTextSize),
                                    static_cast<int>(kTextSize));
        gtk_container_add(GTK_CONTAINER(menu_button_), arrow_);
      }
      gtk_widget_set_size_request(menu_button_, -1, -1);
      gtk_widget_show(arrow_);
    } else {
      InitNineBoxes();
      gtk_widget_set_size_request(menu_button_, kMenuButtonWidth, 0);
      if (arrow_)
        gtk_widget_hide(arrow_);
    }
    UpdateNameLabel();
    UpdateStatusLabel(status_text_);
    UpdateDangerWarning();
  }
}
void DownloadItemGtk::UpdateDownloadProgress() {
  progress_angle_ =
      (progress_angle_ + DownloadShelf::kUnknownIncrementDegrees) %
      DownloadShelf::kMaxDegrees;
  gtk_widget_queue_draw(progress_area_.get());
}
void DownloadItemGtk::StartDownloadProgress() {
  if (progress_timer_.IsRunning())
    return;
  progress_timer_.Start(FROM_HERE,
      base::TimeDelta::FromMilliseconds(DownloadShelf::kProgressRateMs), this,
      &DownloadItemGtk::UpdateDownloadProgress);
}
void DownloadItemGtk::StopDownloadProgress() {
  progress_timer_.Stop();
}
void DownloadItemGtk::OnLoadSmallIconComplete(gfx::Image* image) {
  icon_small_ = image;
  gtk_widget_queue_draw(progress_area_.get());
}
void DownloadItemGtk::OnLoadLargeIconComplete(gfx::Image* image) {
  icon_large_ = image;
  if (download()->GetState() == DownloadItem::COMPLETE)
    DownloadItemDrag::SetSource(body_.get(), download(), icon_large_);
  
  
}
void DownloadItemGtk::LoadIcon() {
  cancelable_task_tracker_.TryCancelAll();
  IconManager* im = g_browser_process->icon_manager();
  icon_filepath_ = download()->GetTargetFilePath();
  im->LoadIcon(icon_filepath_,
               IconLoader::SMALL,
               base::Bind(&DownloadItemGtk::OnLoadSmallIconComplete,
                          base::Unretained(this)),
               &cancelable_task_tracker_);
  im->LoadIcon(icon_filepath_,
               IconLoader::LARGE,
               base::Bind(&DownloadItemGtk::OnLoadLargeIconComplete,
                          base::Unretained(this)),
               &cancelable_task_tracker_);
}
void DownloadItemGtk::UpdateTooltip() {
  const gfx::FontList& font_list =
      ui::ResourceBundle::GetSharedInstance().GetFontList(
          ui::ResourceBundle::BaseFont);
  base::string16 tooltip_text =
      download_model_.GetTooltipText(font_list, kTooltipMaxWidth);
  gtk_widget_set_tooltip_text(body_.get(),
                              base::UTF16ToUTF8(tooltip_text).c_str());
}
void DownloadItemGtk::UpdateNameLabel() {
  const gfx::FontList& font_list =
      ui::ResourceBundle::GetSharedInstance().GetFontList(
          ui::ResourceBundle::BaseFont);
  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);
  }
  GdkColor color = theme_service_->GetGdkColor(
      ThemeProperties::COLOR_BOOKMARK_TEXT);
  gtk_util::SetLabelColor(
      name_label_,
      theme_service_->UsingNativeTheme() ? NULL : &color);
  gtk_label_set_text(GTK_LABEL(name_label_),
                     base::UTF16ToUTF8(filename).c_str());
}
void DownloadItemGtk::UpdateStatusLabel(const std::string& status_text) {
  if (!text_stack_) {
    
    
    return;
  }
  
  
  
  
  if (status_text.empty()) {
    if (status_label_)
      gtk_widget_destroy(status_label_);
    return;
  }
  if (!status_label_) {
    status_label_ = gtk_label_new(NULL);
    g_signal_connect(status_label_, "destroy",
                     G_CALLBACK(gtk_widget_destroyed), &status_label_);
    
    gtk_misc_set_alignment(GTK_MISC(status_label_), 0, 0.5);
    
    gtk_util::ForceFontSizePixels(status_label_, kTextSize);
    gtk_box_pack_start(GTK_BOX(text_stack_), status_label_, FALSE, FALSE, 0);
    gtk_widget_show_all(status_label_);
  }
  GdkColor text_color;
  if (!theme_service_->UsingNativeTheme()) {
    SkColor color = theme_service_->GetColor(
        ThemeProperties::COLOR_BOOKMARK_TEXT);
    if (color_utils::RelativeLuminance(color) > 0.5) {
      color = SkColorSetRGB(
          static_cast<int>(kDownloadItemLuminanceMod *
                           SkColorGetR(color)),
          static_cast<int>(kDownloadItemLuminanceMod *
                           SkColorGetG(color)),
          static_cast<int>(kDownloadItemLuminanceMod *
                           SkColorGetB(color)));
    }
    
    
    SkColor blend_color = SkColorSetRGB(241, 245, 250);
    text_color = gfx::SkColorToGdkColor(
        color_utils::AlphaBlend(blend_color, color, 77));
  }
  gtk_util::SetLabelColor(
      status_label_,
      theme_service_->UsingNativeTheme() ? NULL : &text_color);
  gtk_label_set_text(GTK_LABEL(status_label_), status_text.c_str());
}
void DownloadItemGtk::UpdateDangerWarning() {
  if (dangerous_prompt_) {
    UpdateDangerIcon();
    
    
    const gfx::FontList& font_list =
        ui::ResourceBundle::GetSharedInstance().GetFontList(
            ui::ResourceBundle::BaseFont);
    base::string16 dangerous_warning =
        download_model_.GetWarningText(font_list, kTextWidth);
    if (theme_service_->UsingNativeTheme()) {
      gtk_util::SetLabelColor(dangerous_label_, NULL);
    } else {
      GdkColor color = theme_service_->GetGdkColor(
          ThemeProperties::COLOR_BOOKMARK_TEXT);
      gtk_util::SetLabelColor(dangerous_label_, &color);
    }
    gtk_label_set_text(GTK_LABEL(dangerous_label_),
                       base::UTF16ToUTF8(dangerous_warning).c_str());
    
    gtk_util::ForceFontSizePixels(dangerous_label_, kTextSize);
    gtk_widget_set_size_request(dangerous_label_, -1, -1);
    gtk_label_set_line_wrap(GTK_LABEL(dangerous_label_), FALSE);
    GtkRequisition req;
    gtk_widget_size_request(dangerous_label_, &req);
    gint label_width = req.width;
    if (req.width > kDangerousTextWidth) {
      
      
      
      gtk_label_set_line_wrap(GTK_LABEL(dangerous_label_), TRUE);
      int full_width = req.width;
      int tenths = 6;
      do {
        label_width = full_width * tenths / 10;
        gtk_widget_set_size_request(dangerous_label_, label_width, -1);
        gtk_widget_size_request(dangerous_label_, &req);
      } while (req.height > kBodyHeight && ++tenths <= 10);
      DCHECK(req.height <= kBodyHeight);
    }
    
    
    gtk_widget_size_request(dangerous_hbox_.get(), &req);
    dangerous_hbox_full_width_ = req.width;
    dangerous_hbox_start_width_ = dangerous_hbox_full_width_ - label_width;
  }
}
void DownloadItemGtk::UpdateDangerIcon() {
  if (theme_service_->UsingNativeTheme()) {
    const char* stock = download_model_.MightBeMalicious() ?
        GTK_STOCK_DIALOG_ERROR : GTK_STOCK_DIALOG_WARNING;
    gtk_image_set_from_stock(
        GTK_IMAGE(dangerous_image_), stock, GTK_ICON_SIZE_SMALL_TOOLBAR);
  } else {
    
    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    int pixbuf_id =
        download_model_.MightBeMalicious() ? IDR_SAFEBROWSING_WARNING
                                           : IDR_WARNING;
    gtk_image_set_from_pixbuf(GTK_IMAGE(dangerous_image_),
                              rb.GetNativeImageNamed(pixbuf_id).ToGdkPixbuf());
  }
}
void DownloadItemGtk::InitNineBoxes() {
  if (body_nine_box_normal_)
    return;
  body_nine_box_normal_ = new NineBox(
      IDR_DOWNLOAD_BUTTON_LEFT_TOP,
      IDR_DOWNLOAD_BUTTON_CENTER_TOP,
      IDR_DOWNLOAD_BUTTON_RIGHT_TOP,
      IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE,
      IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE,
      IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE,
      IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM,
      IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM,
      IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM);
  body_nine_box_prelight_ = new NineBox(
      IDR_DOWNLOAD_BUTTON_LEFT_TOP_H,
      IDR_DOWNLOAD_BUTTON_CENTER_TOP_H,
      IDR_DOWNLOAD_BUTTON_RIGHT_TOP_H,
      IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_H,
      IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_H,
      IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_H,
      IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_H,
      IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_H,
      IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_H);
  body_nine_box_active_ = new NineBox(
      IDR_DOWNLOAD_BUTTON_LEFT_TOP_P,
      IDR_DOWNLOAD_BUTTON_CENTER_TOP_P,
      IDR_DOWNLOAD_BUTTON_RIGHT_TOP_P,
      IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_P,
      IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_P,
      IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_P,
      IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_P,
      IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_P,
      IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_P);
  menu_nine_box_normal_ = new NineBox(
      IDR_DOWNLOAD_BUTTON_MENU_TOP, 0, 0,
      IDR_DOWNLOAD_BUTTON_MENU_MIDDLE, 0, 0,
      IDR_DOWNLOAD_BUTTON_MENU_BOTTOM, 0, 0);
  menu_nine_box_prelight_ = new NineBox(
      IDR_DOWNLOAD_BUTTON_MENU_TOP_H, 0, 0,
      IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_H, 0, 0,
      IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_H, 0, 0);
  menu_nine_box_active_ = new NineBox(
      IDR_DOWNLOAD_BUTTON_MENU_TOP_P, 0, 0,
      IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_P, 0, 0,
      IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_P, 0, 0);
  dangerous_nine_box_ = new NineBox(
      IDR_DOWNLOAD_BUTTON_LEFT_TOP,
      IDR_DOWNLOAD_BUTTON_CENTER_TOP,
      IDR_DOWNLOAD_BUTTON_RIGHT_TOP_NO_DD,
      IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE,
      IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE,
      IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_NO_DD,
      IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM,
      IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM,
      IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_NO_DD);
}
gboolean DownloadItemGtk::OnHboxExpose(GtkWidget* widget, GdkEventExpose* e) {
  TRACE_EVENT0("ui::gtk", "DownloadItemGtk::OnHboxExpose");
  if (theme_service_->UsingNativeTheme()) {
    GtkAllocation allocation;
    gtk_widget_get_allocation(widget, &allocation);
    int border_width = gtk_container_get_border_width(GTK_CONTAINER(widget));
    int x = allocation.x + border_width;
    int y = allocation.y + border_width;
    int width = allocation.width - border_width * 2;
    int height = allocation.height - border_width * 2;
    if (download_model_.IsDangerous()) {
      
      gtk_paint_shadow(gtk_widget_get_style(widget),
                       gtk_widget_get_window(widget),
                       gtk_widget_get_state(widget),
                       static_cast<GtkShadowType>(GTK_SHADOW_OUT),
                       &e->area, widget, "frame",
                       x, y, width, height);
    } else {
      
      
      
      
      
      
      
      GtkStyle* style = gtk_widget_get_style(body_.get());
      GtkAllocation left_clip;
      gtk_widget_get_allocation(body_.get(), &left_clip);
      GtkAllocation right_clip;
      gtk_widget_get_allocation(menu_button_, &right_clip);
      GtkShadowType body_shadow =
          GTK_BUTTON(body_.get())->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
      gtk_paint_box(style,
                    gtk_widget_get_window(widget),
                    gtk_widget_get_state(body_.get()),
                    body_shadow,
                    &left_clip, widget, "button",
                    x, y, width, height);
      GtkShadowType menu_shadow =
          GTK_BUTTON(menu_button_)->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
      gtk_paint_box(style,
                    gtk_widget_get_window(widget),
                    gtk_widget_get_state(menu_button_),
                    menu_shadow,
                    &right_clip, widget, "button",
                    x, y, width, height);
      
      
      
      
      GtkAllocation arrow_allocation;
      gtk_widget_get_allocation(arrow_, &arrow_allocation);
      gtk_paint_vline(style,
                      gtk_widget_get_window(widget),
                      gtk_widget_get_state(widget),
                      &e->area, widget, "button",
                      arrow_allocation.y,
                      arrow_allocation.y + arrow_allocation.height,
                      left_clip.x + left_clip.width);
    }
  }
  return FALSE;
}
gboolean DownloadItemGtk::OnExpose(GtkWidget* widget, GdkEventExpose* e) {
  TRACE_EVENT0("ui::gtk", "DownloadItemGtk::OnExpose");
  if (!theme_service_->UsingNativeTheme()) {
    bool is_body = widget == body_.get();
    NineBox* nine_box = NULL;
    
    if (gtk_widget_get_state(widget) == GTK_STATE_PRELIGHT)
      nine_box = is_body ? body_nine_box_prelight_ : menu_nine_box_prelight_;
    else if (gtk_widget_get_state(widget) == GTK_STATE_ACTIVE)
      nine_box = is_body ? body_nine_box_active_ : menu_nine_box_active_;
    else
      nine_box = is_body ? body_nine_box_normal_ : menu_nine_box_normal_;
    
    
    
    if (!is_body && menu_showing_)
      nine_box = menu_nine_box_active_;
    nine_box->RenderToWidget(widget);
  }
  GtkWidget* child = gtk_bin_get_child(GTK_BIN(widget));
  if (child)
    gtk_container_propagate_expose(GTK_CONTAINER(widget), child, e);
  return TRUE;
}
void DownloadItemGtk::ReenableHbox() {
  gtk_widget_set_sensitive(hbox_.get(), true);
  disabled_while_opening_ = false;
  UpdateNameLabel();
}
void DownloadItemGtk::OnDownloadOpened(DownloadItem* download) {
  disabled_while_opening_ = true;
  gtk_widget_set_sensitive(hbox_.get(), false);
  base::MessageLoop::current()->PostDelayedTask(
      FROM_HERE,
      base::Bind(&DownloadItemGtk::ReenableHbox,
                 weak_ptr_factory_.GetWeakPtr()),
      base::TimeDelta::FromMilliseconds(kDisabledOnOpenDurationMs));
  UpdateNameLabel();
  parent_shelf_->ItemOpened();
}
void DownloadItemGtk::OnClick(GtkWidget* widget) {
  UMA_HISTOGRAM_LONG_TIMES("clickjacking.open_download",
                           base::Time::Now() - creation_time_);
  download()->OpenDownload();
}
gboolean DownloadItemGtk::OnButtonPress(GtkWidget* button,
                                        GdkEventButton* event) {
  if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
    ShowPopupMenu(NULL, event);
    return TRUE;
  }
  return FALSE;
}
gboolean DownloadItemGtk::OnProgressAreaExpose(GtkWidget* widget,
                                               GdkEventExpose* event) {
  TRACE_EVENT0("ui::gtk", "DownloadItemGtk::OnProgressAreaExpose");
  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);
  
  gfx::CanvasSkiaPaint canvas(event, false);
  DownloadItem::DownloadState state = download()->GetState();
  if (complete_animation_.is_animating()) {
    if (state == DownloadItem::INTERRUPTED) {
      DownloadShelf::PaintDownloadInterrupted(
          &canvas,
          allocation.x,
          allocation.y,
          complete_animation_.GetCurrentValue(),
          DownloadShelf::SMALL);
    } else {
      DownloadShelf::PaintDownloadComplete(
          &canvas,
          allocation.x,
          allocation.y,
          complete_animation_.GetCurrentValue(),
          DownloadShelf::SMALL);
    }
  } else if (state == DownloadItem::IN_PROGRESS) {
    DownloadShelf::PaintDownloadProgress(&canvas,
                                         allocation.x,
                                         allocation.y,
                                         progress_angle_,
                                         download_model_.PercentComplete(),
                                         DownloadShelf::SMALL);
  }
  
  
  
  if (icon_small_) {
    const int offset = DownloadShelf::kSmallProgressIconOffset;
    canvas.DrawImageInt(icon_small_->AsImageSkia(),
        allocation.x + offset, allocation.y + offset);
  }
  return TRUE;
}
gboolean DownloadItemGtk::OnMenuButtonPressEvent(GtkWidget* button,
                                                 GdkEventButton* event) {
  if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
    ShowPopupMenu(button, event);
    menu_showing_ = true;
    gtk_widget_queue_draw(button);
    return TRUE;
  }
  return FALSE;
}
void DownloadItemGtk::ShowPopupMenu(GtkWidget* button,
                                    GdkEventButton* event) {
  
  if (complete_animation_.is_animating())
    complete_animation_.End();
  if (!menu_.get()) {
    menu_.reset(new DownloadShelfContextMenuGtk(this,
                                                parent_shelf_->GetNavigator()));
  }
  menu_->Popup(button, event);
}
gboolean DownloadItemGtk::OnDangerousPromptExpose(GtkWidget* widget,
                                                  GdkEventExpose* event) {
  TRACE_EVENT0("ui::gtk", "DownloadItemGtk::OnDangerousPromptExpose");
  if (!theme_service_->UsingNativeTheme()) {
    
    dangerous_nine_box_->RenderToWidget(widget);
  }
  return FALSE;  
}
void DownloadItemGtk::OnDangerousAccept(GtkWidget* button) {
  UMA_HISTOGRAM_LONG_TIMES("clickjacking.save_download",
                           base::Time::Now() - creation_time_);
  download()->ValidateDangerousDownload();
}
void DownloadItemGtk::OnDangerousDecline(GtkWidget* button) {
  UMA_HISTOGRAM_LONG_TIMES("clickjacking.discard_download",
                           base::Time::Now() - creation_time_);
  download()->Remove();
}