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 34InfoBar* ExtensionInfoBarDelegate::CreateInfoBar(InfoBarService* owner) { 35 return new ExtensionInfoBarGtk(owner, this); 36} 37 38 39// ExtensionInfoBarGtk -------------------------------------------------------- 40 41ExtensionInfoBarGtk::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 56ExtensionInfoBarGtk::~ExtensionInfoBarGtk() { 57 if (GetDelegate()) 58 GetDelegate()->set_observer(NULL); 59} 60 61void ExtensionInfoBarGtk::PlatformSpecificHide(bool animate) { 62 DCHECK(view_); 63 DCHECK(alignment_); 64 gtk_util::RemoveAllChildren(alignment_); 65} 66 67void 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 73void ExtensionInfoBarGtk::GetBottomColor(InfoBarDelegate::Type type, 74 double* r, double* g, double* b) { 75 *r = *g = *b = 218.0 / 255.0; 76} 77 78void 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 144void ExtensionInfoBarGtk::StoppedShowing() { 145 if (button_) 146 gtk_chrome_button_unset_paint_state(GTK_CHROME_BUTTON(button_)); 147} 148 149void ExtensionInfoBarGtk::OnDelegateDeleted() { 150 delegate_ = NULL; 151} 152 153void 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 189ExtensionInfoBarDelegate* ExtensionInfoBarGtk::GetDelegate() { 190 return delegate_ ? delegate_->AsExtensionInfoBarDelegate() : NULL; 191} 192 193Browser* 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 202ExtensionContextMenuModel* 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 214void 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 222gboolean 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 240gboolean 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