extension_infobar.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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/views/infobars/extension_infobar.h" 6 7#include "chrome/browser/extensions/extension_context_menu_model.h" 8#include "chrome/browser/extensions/extension_infobar_delegate.h" 9#include "chrome/browser/extensions/extension_view_host.h" 10#include "chrome/browser/extensions/image_loader.h" 11#include "chrome/browser/platform_util.h" 12#include "chrome/browser/ui/views/frame/browser_view.h" 13#include "extensions/common/constants.h" 14#include "extensions/common/extension.h" 15#include "extensions/common/extension_icon_set.h" 16#include "extensions/common/extension_resource.h" 17#include "extensions/common/manifest_handlers/icons_handler.h" 18#include "grit/theme_resources.h" 19#include "ui/base/resource/resource_bundle.h" 20#include "ui/gfx/animation/slide_animation.h" 21#include "ui/gfx/canvas.h" 22#include "ui/gfx/image/canvas_image_source.h" 23#include "ui/gfx/image/image.h" 24#include "ui/views/controls/button/menu_button.h" 25#include "ui/views/controls/image_view.h" 26#include "ui/views/controls/menu/menu_item_view.h" 27#include "ui/views/widget/widget.h" 28 29 30// ExtensionInfoBarDelegate ---------------------------------------------------- 31 32// static 33scoped_ptr<infobars::InfoBar> ExtensionInfoBarDelegate::CreateInfoBar( 34 scoped_ptr<ExtensionInfoBarDelegate> delegate) { 35 Browser* browser = delegate->browser_; 36 return scoped_ptr<infobars::InfoBar>( 37 new ExtensionInfoBar(delegate.Pass(), browser)); 38} 39 40 41// ExtensionInfoBar ------------------------------------------------------------ 42 43namespace { 44// The horizontal margin between the infobar icon and the Extension (HTML) view. 45const int kIconHorizontalMargin = 1; 46 47class MenuImageSource: public gfx::CanvasImageSource { 48 public: 49 MenuImageSource(const gfx::ImageSkia& icon, const gfx::ImageSkia& drop_image) 50 : gfx::CanvasImageSource(ComputeSize(drop_image), false), 51 icon_(icon), 52 drop_image_(drop_image) { 53 } 54 55 virtual ~MenuImageSource() { 56 } 57 58 // Overridden from gfx::CanvasImageSource 59 virtual void Draw(gfx::Canvas* canvas) OVERRIDE { 60 int image_size = extension_misc::EXTENSION_ICON_BITTY; 61 canvas->DrawImageInt(icon_, 0, 0, icon_.width(), icon_.height(), 0, 0, 62 image_size, image_size, false); 63 canvas->DrawImageInt(drop_image_, image_size + kDropArrowLeftMargin, 64 image_size / 2); 65 } 66 67 private: 68 gfx::Size ComputeSize(const gfx::ImageSkia& drop_image) const { 69 int image_size = extension_misc::EXTENSION_ICON_BITTY; 70 return gfx::Size(image_size + kDropArrowLeftMargin + drop_image.width(), 71 image_size); 72 } 73 74 // The margin between the extension icon and the drop-down arrow image. 75 static const int kDropArrowLeftMargin = 3; 76 77 const gfx::ImageSkia icon_; 78 const gfx::ImageSkia drop_image_; 79 80 DISALLOW_COPY_AND_ASSIGN(MenuImageSource); 81}; 82 83} // namespace 84 85ExtensionInfoBar::ExtensionInfoBar( 86 scoped_ptr<ExtensionInfoBarDelegate> delegate, 87 Browser* browser) 88 : InfoBarView(delegate.PassAs<infobars::InfoBarDelegate>()), 89 browser_(browser), 90 infobar_icon_(NULL), 91 icon_as_menu_(NULL), 92 icon_as_image_(NULL), 93 weak_ptr_factory_(this) { 94} 95 96ExtensionInfoBar::~ExtensionInfoBar() { 97} 98 99void ExtensionInfoBar::Layout() { 100 InfoBarView::Layout(); 101 102 infobar_icon_->SetPosition(gfx::Point(StartX(), OffsetY(infobar_icon_))); 103 ExtensionViewViews* extension_view = 104 GetDelegate()->extension_view_host()->view(); 105 // TODO(pkasting): We'd like to simply set the extension view's desired height 106 // at creation time and position using OffsetY() like for other infobar items, 107 // but the NativeViewHost inside does not seem to be clipped by the ClipRect() 108 // call in InfoBarView::PaintChildren(), so we have to manually clamp the size 109 // here. 110 extension_view->SetSize( 111 gfx::Size(std::max(0, EndX() - StartX() - NonExtensionViewWidth()), 112 std::min(height() - kSeparatorLineHeight - arrow_height(), 113 GetDelegate()->height()))); 114 // We do SetPosition() separately after SetSize() so OffsetY() will work. 115 extension_view->SetPosition( 116 gfx::Point(infobar_icon_->bounds().right() + kIconHorizontalMargin, 117 std::max(arrow_height(), OffsetY(extension_view)))); 118} 119 120void ExtensionInfoBar::ViewHierarchyChanged( 121 const ViewHierarchyChangedDetails& details) { 122 if (!details.is_add || (details.child != this) || (infobar_icon_ != NULL)) { 123 InfoBarView::ViewHierarchyChanged(details); 124 return; 125 } 126 127 extensions::ExtensionViewHost* extension_view_host = 128 GetDelegate()->extension_view_host(); 129 130 if (extension_view_host->extension()->ShowConfigureContextMenus()) { 131 icon_as_menu_ = new views::MenuButton(NULL, base::string16(), this, false); 132 icon_as_menu_->SetFocusable(true); 133 infobar_icon_ = icon_as_menu_; 134 } else { 135 icon_as_image_ = new views::ImageView(); 136 infobar_icon_ = icon_as_image_; 137 } 138 139 // Wait until the icon image is loaded before showing it. 140 infobar_icon_->SetVisible(false); 141 AddChildView(infobar_icon_); 142 143 // Set the desired height of the ExtensionViewViews, so that when the 144 // AddChildView() call triggers InfoBarView::ViewHierarchyChanged(), it can 145 // read the correct height off this object in order to calculate the overall 146 // desired infobar height. 147 extension_view_host->view()->SetSize(gfx::Size(0, GetDelegate()->height())); 148 AddChildView(extension_view_host->view()); 149 150 // This must happen after adding all other children so InfoBarView can ensure 151 // the close button is the last child. 152 InfoBarView::ViewHierarchyChanged(details); 153 154 // This must happen after adding all children because it can trigger layout, 155 // which assumes that particular children (e.g. the close button) have already 156 // been added. 157 const extensions::Extension* extension = extension_view_host->extension(); 158 extension_misc::ExtensionIcons image_size = 159 extension_misc::EXTENSION_ICON_BITTY; 160 extensions::ExtensionResource icon_resource = 161 extensions::IconsInfo::GetIconResource( 162 extension, image_size, ExtensionIconSet::MATCH_EXACTLY); 163 extensions::ImageLoader* loader = 164 extensions::ImageLoader::Get(extension_view_host->browser_context()); 165 loader->LoadImageAsync( 166 extension, 167 icon_resource, 168 gfx::Size(image_size, image_size), 169 base::Bind(&ExtensionInfoBar::OnImageLoaded, 170 weak_ptr_factory_.GetWeakPtr())); 171} 172 173int ExtensionInfoBar::ContentMinimumWidth() { 174 return NonExtensionViewWidth() + 175 GetDelegate()->extension_view_host()->view()->GetMinimumSize().width(); 176} 177 178void ExtensionInfoBar::OnMenuButtonClicked(views::View* source, 179 const gfx::Point& point) { 180 if (!owner()) 181 return; // We're closing; don't call anything, it might access the owner. 182 const extensions::Extension* extension = 183 GetDelegate()->extension_view_host()->extension(); 184 DCHECK(icon_as_menu_); 185 186 scoped_refptr<ExtensionContextMenuModel> options_menu_contents = 187 new ExtensionContextMenuModel(extension, browser_); 188 DCHECK_EQ(icon_as_menu_, source); 189 RunMenuAt(options_menu_contents.get(), 190 icon_as_menu_, 191 views::MenuItemView::TOPLEFT); 192} 193 194void ExtensionInfoBar::OnImageLoaded(const gfx::Image& image) { 195 if (!GetDelegate()) 196 return; // The delegate can go away while we asynchronously load images. 197 198 const gfx::ImageSkia* icon = NULL; 199 // Fall back on the default extension icon on failure. 200 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 201 if (image.IsEmpty()) 202 icon = rb.GetImageNamed(IDR_EXTENSIONS_SECTION).ToImageSkia(); 203 else 204 icon = image.ToImageSkia(); 205 206 if (icon_as_menu_) { 207 const gfx::ImageSkia* drop_image = 208 rb.GetImageNamed(IDR_APP_DROPARROW).ToImageSkia(); 209 210 gfx::CanvasImageSource* source = new MenuImageSource(*icon, *drop_image); 211 gfx::ImageSkia menu_image = gfx::ImageSkia(source, source->size()); 212 icon_as_menu_->SetIcon(menu_image); 213 } else { 214 icon_as_image_->SetImage(*icon); 215 } 216 217 infobar_icon_->SizeToPreferredSize(); 218 infobar_icon_->SetVisible(true); 219 220 Layout(); 221} 222 223ExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() { 224 return delegate()->AsExtensionInfoBarDelegate(); 225} 226 227int ExtensionInfoBar::NonExtensionViewWidth() const { 228 return infobar_icon_->width() + kIconHorizontalMargin; 229} 230