Home | History | Annotate | Download | only in infobars
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/gtk/infobars/extension_infobar_gtk.h"
      6 
      7 #include "base/debug/trace_event.h"
      8 #include "chrome/browser/extensions/extension_context_menu_model.h"
      9 #include "chrome/browser/extensions/extension_host.h"
     10 #include "chrome/browser/extensions/image_loader.h"
     11 #include "chrome/browser/platform_util.h"
     12 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
     13 #include "chrome/browser/ui/gtk/custom_button.h"
     14 #include "chrome/browser/ui/gtk/gtk_chrome_button.h"
     15 #include "chrome/browser/ui/gtk/gtk_util.h"
     16 #include "chrome/browser/ui/gtk/infobars/infobar_container_gtk.h"
     17 #include "chrome/common/extensions/extension.h"
     18 #include "chrome/common/extensions/extension_constants.h"
     19 #include "chrome/common/extensions/extension_icon_set.h"
     20 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
     21 #include "content/public/browser/render_view_host.h"
     22 #include "content/public/browser/render_widget_host_view.h"
     23 #include "extensions/common/extension_resource.h"
     24 #include "grit/theme_resources.h"
     25 #include "ui/base/gtk/gtk_signal_registrar.h"
     26 #include "ui/base/resource/resource_bundle.h"
     27 #include "ui/gfx/canvas.h"
     28 #include "ui/gfx/gtk_util.h"
     29 #include "ui/gfx/image/image.h"
     30 
     31 
     32 // ExtensionInfoBarDelegate ---------------------------------------------------
     33 
     34 InfoBar* ExtensionInfoBarDelegate::CreateInfoBar(InfoBarService* owner) {
     35   return new ExtensionInfoBarGtk(owner, this);
     36 }
     37 
     38 
     39 // ExtensionInfoBarGtk --------------------------------------------------------
     40 
     41 ExtensionInfoBarGtk::ExtensionInfoBarGtk(InfoBarService* owner,
     42                                          ExtensionInfoBarDelegate* delegate)
     43     : InfoBarGtk(owner, delegate),
     44       delegate_(delegate),
     45       view_(NULL),
     46       button_(NULL),
     47       icon_(NULL),
     48       alignment_(NULL),
     49       weak_ptr_factory_(this) {
     50   GetDelegate()->set_observer(this);
     51 
     52   int height = GetDelegate()->height();
     53   SetBarTargetHeight((height > 0) ? (height + kSeparatorLineHeight) : 0);
     54 }
     55 
     56 ExtensionInfoBarGtk::~ExtensionInfoBarGtk() {
     57   if (GetDelegate())
     58     GetDelegate()->set_observer(NULL);
     59 }
     60 
     61 void ExtensionInfoBarGtk::PlatformSpecificHide(bool animate) {
     62   DCHECK(view_);
     63   DCHECK(alignment_);
     64   gtk_util::RemoveAllChildren(alignment_);
     65 }
     66 
     67 void ExtensionInfoBarGtk::GetTopColor(InfoBarDelegate::Type type,
     68                                       double* r, double* g, double* b) {
     69   // Extension infobars are always drawn with chrome-theme colors.
     70   *r = *g = *b = 233.0 / 255.0;
     71 }
     72 
     73 void ExtensionInfoBarGtk::GetBottomColor(InfoBarDelegate::Type type,
     74                                          double* r, double* g, double* b) {
     75   *r = *g = *b = 218.0 / 255.0;
     76 }
     77 
     78 void ExtensionInfoBarGtk::InitWidgets() {
     79   InfoBarGtk::InitWidgets();
     80 
     81   // Always render the close button as if we were doing chrome style widget
     82   // rendering. For extension infobars, we force chrome style rendering because
     83   // extension authors are going to expect to match the declared gradient in
     84   // extensions_infobar.css, and the close button provided by some GTK+ themes
     85   // won't look good on this background.
     86   ForceCloseButtonToUseChromeTheme();
     87 
     88   icon_ = gtk_image_new();
     89   gtk_misc_set_alignment(GTK_MISC(icon_), 0.5, 0.5);
     90 
     91   extensions::ExtensionHost* extension_host = GetDelegate()->extension_host();
     92   const extensions::Extension* extension = extension_host->extension();
     93 
     94   if (extension->ShowConfigureContextMenus()) {
     95     button_ = gtk_chrome_button_new();
     96     gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(button_), FALSE);
     97     g_object_set_data(G_OBJECT(button_), "left-align-popup",
     98                       reinterpret_cast<void*>(true));
     99 
    100     gtk_button_set_image(GTK_BUTTON(button_), icon_);
    101     gtk_util::CenterWidgetInHBox(hbox(), button_, false, 0);
    102   } else {
    103     gtk_util::CenterWidgetInHBox(hbox(), icon_, false, 0);
    104   }
    105 
    106   // Start loading the image for the menu button.
    107   extensions::ExtensionResource icon_resource =
    108       extensions::IconsInfo::GetIconResource(
    109           extension,
    110           extension_misc::EXTENSION_ICON_BITTY,
    111           ExtensionIconSet::MATCH_EXACTLY);
    112   // Load image asynchronously, calling back OnImageLoaded.
    113   extensions::ImageLoader* loader =
    114       extensions::ImageLoader::Get(extension_host->profile());
    115   loader->LoadImageAsync(extension, icon_resource,
    116                          gfx::Size(extension_misc::EXTENSION_ICON_BITTY,
    117                                    extension_misc::EXTENSION_ICON_BITTY),
    118                          base::Bind(&ExtensionInfoBarGtk::OnImageLoaded,
    119                                     weak_ptr_factory_.GetWeakPtr()));
    120 
    121   // Pad the bottom of the infobar by one pixel for the border.
    122   alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
    123   gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_), 0, 1, 0, 0);
    124   gtk_box_pack_start(GTK_BOX(hbox()), alignment_, TRUE, TRUE, 0);
    125 
    126   view_ = extension_host->view();
    127 
    128   if (gtk_widget_get_parent(view_->native_view())) {
    129     gtk_widget_reparent(view_->native_view(), alignment_);
    130   } else {
    131     gtk_container_add(GTK_CONTAINER(alignment_), view_->native_view());
    132   }
    133 
    134   if (button_) {
    135     signals()->Connect(button_, "button-press-event",
    136                        G_CALLBACK(&OnButtonPressThunk), this);
    137   }
    138   signals()->Connect(view_->native_view(), "expose-event",
    139                      G_CALLBACK(&OnExposeThunk), this);
    140   signals()->Connect(view_->native_view(), "size_allocate",
    141                      G_CALLBACK(&OnSizeAllocateThunk), this);
    142 }
    143 
    144 void ExtensionInfoBarGtk::StoppedShowing() {
    145   if (button_)
    146     gtk_chrome_button_unset_paint_state(GTK_CHROME_BUTTON(button_));
    147 }
    148 
    149 void ExtensionInfoBarGtk::OnDelegateDeleted() {
    150   delegate_ = NULL;
    151 }
    152 
    153 void ExtensionInfoBarGtk::OnImageLoaded(const gfx::Image& image) {
    154 
    155   DCHECK(icon_);
    156   // TODO(erg): IDR_EXTENSIONS_SECTION should have an IDR_INFOBAR_EXTENSIONS
    157   // icon of the correct size with real subpixel shading and such.
    158   const gfx::ImageSkia* icon = NULL;
    159   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    160   if (image.IsEmpty())
    161     icon = rb.GetImageSkiaNamed(IDR_EXTENSIONS_SECTION);
    162   else
    163     icon = image.ToImageSkia();
    164 
    165   SkBitmap bitmap;
    166   if (button_) {
    167     gfx::ImageSkia* drop_image = rb.GetImageSkiaNamed(IDR_APP_DROPARROW);
    168 
    169     int image_size = extension_misc::EXTENSION_ICON_BITTY;
    170     // The margin between the extension icon and the drop-down arrow bitmap.
    171     static const int kDropArrowLeftMargin = 3;
    172     scoped_ptr<gfx::Canvas> canvas(new gfx::Canvas(
    173         gfx::Size(image_size + kDropArrowLeftMargin + drop_image->width(),
    174                   image_size), ui::SCALE_FACTOR_100P, false));
    175     canvas->DrawImageInt(*icon, 0, 0, icon->width(), icon->height(), 0, 0,
    176                          image_size, image_size, false);
    177     canvas->DrawImageInt(*drop_image, image_size + kDropArrowLeftMargin,
    178                          image_size / 2);
    179     bitmap = canvas->ExtractImageRep().sk_bitmap();
    180   } else {
    181     bitmap = *icon->bitmap();
    182   }
    183 
    184   GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(bitmap);
    185   gtk_image_set_from_pixbuf(GTK_IMAGE(icon_), pixbuf);
    186   g_object_unref(pixbuf);
    187 }
    188 
    189 ExtensionInfoBarDelegate* ExtensionInfoBarGtk::GetDelegate() {
    190   return delegate_ ? delegate_->AsExtensionInfoBarDelegate() : NULL;
    191 }
    192 
    193 Browser* ExtensionInfoBarGtk::GetBrowser() {
    194   DCHECK(icon_);
    195   // Get the Browser object this infobar is attached to.
    196   GtkWindow* parent = platform_util::GetTopLevel(icon_);
    197   return parent ?
    198       BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent)->browser() :
    199       NULL;
    200 }
    201 
    202 ExtensionContextMenuModel* ExtensionInfoBarGtk::BuildMenuModel() {
    203   const extensions::Extension* extension = GetDelegate()->extension();
    204   if (!extension->ShowConfigureContextMenus())
    205     return NULL;
    206 
    207   Browser* browser = GetBrowser();
    208   if (!browser)
    209     return NULL;
    210 
    211   return new ExtensionContextMenuModel(extension, browser);
    212 }
    213 
    214 void ExtensionInfoBarGtk::OnSizeAllocate(GtkWidget* widget,
    215                                          GtkAllocation* allocation) {
    216   gfx::Size new_size(allocation->width, allocation->height);
    217 
    218   GetDelegate()->extension_host()->view()->render_view_host()->GetView()->
    219       SetSize(new_size);
    220 }
    221 
    222 gboolean ExtensionInfoBarGtk::OnButtonPress(GtkWidget* widget,
    223                                             GdkEventButton* event) {
    224   if (event->button != 1)
    225     return FALSE;
    226 
    227   DCHECK(button_);
    228 
    229   context_menu_model_ = BuildMenuModel();
    230   if (!context_menu_model_.get())
    231     return FALSE;
    232 
    233   gtk_chrome_button_set_paint_state(GTK_CHROME_BUTTON(widget),
    234                                     GTK_STATE_ACTIVE);
    235   ShowMenuWithModel(widget, this, context_menu_model_.get());
    236 
    237   return TRUE;
    238 }
    239 
    240 gboolean ExtensionInfoBarGtk::OnExpose(GtkWidget* sender,
    241                                        GdkEventExpose* event) {
    242   TRACE_EVENT0("ui::gtk", "ExtensionInfoBarGtk::OnExpose");
    243 
    244   // We also need to draw our infobar arrows over the renderer.
    245   static_cast<InfoBarContainerGtk*>(container())->
    246       PaintInfobarBitsOn(sender, event, this);
    247 
    248   return FALSE;
    249 }
    250