extension_infobar.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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 "chrome/common/extensions/extension_constants.h" 14#include "chrome/common/extensions/extension_icon_set.h" 15#include "chrome/common/extensions/manifest_handlers/icons_handler.h" 16#include "extensions/common/extension.h" 17#include "extensions/common/extension_resource.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<InfoBar> ExtensionInfoBarDelegate::CreateInfoBar( 34 scoped_ptr<ExtensionInfoBarDelegate> delegate) { 35 Browser* browser = delegate->browser_; 36 return scoped_ptr<InfoBar>(new ExtensionInfoBar(delegate.Pass(), browser)); 37} 38 39 40// ExtensionInfoBar ------------------------------------------------------------ 41 42namespace { 43// The horizontal margin between the infobar icon and the Extension (HTML) view. 44const int kIconHorizontalMargin = 1; 45 46class MenuImageSource: public gfx::CanvasImageSource { 47 public: 48 MenuImageSource(const gfx::ImageSkia& icon, const gfx::ImageSkia& drop_image) 49 : gfx::CanvasImageSource(ComputeSize(drop_image), false), 50 icon_(icon), 51 drop_image_(drop_image) { 52 } 53 54 virtual ~MenuImageSource() { 55 } 56 57 // Overridden from gfx::CanvasImageSource 58 virtual void Draw(gfx::Canvas* canvas) OVERRIDE { 59 int image_size = extension_misc::EXTENSION_ICON_BITTY; 60 canvas->DrawImageInt(icon_, 0, 0, icon_.width(), icon_.height(), 0, 0, 61 image_size, image_size, false); 62 canvas->DrawImageInt(drop_image_, image_size + kDropArrowLeftMargin, 63 image_size / 2); 64 } 65 66 private: 67 gfx::Size ComputeSize(const gfx::ImageSkia& drop_image) const { 68 int image_size = extension_misc::EXTENSION_ICON_BITTY; 69 return gfx::Size(image_size + kDropArrowLeftMargin + drop_image.width(), 70 image_size); 71 } 72 73 // The margin between the extension icon and the drop-down arrow image. 74 static const int kDropArrowLeftMargin = 3; 75 76 const gfx::ImageSkia icon_; 77 const gfx::ImageSkia drop_image_; 78 79 DISALLOW_COPY_AND_ASSIGN(MenuImageSource); 80}; 81 82} // namespace 83 84ExtensionInfoBar::ExtensionInfoBar( 85 scoped_ptr<ExtensionInfoBarDelegate> delegate, 86 Browser* browser) 87 : InfoBarView(delegate.PassAs<InfoBarDelegate>()), 88 browser_(browser), 89 infobar_icon_(NULL), 90 icon_as_menu_(NULL), 91 icon_as_image_(NULL), 92 weak_ptr_factory_(this) { 93 int height = GetDelegate()->height(); 94 SetBarTargetHeight((height > 0) ? (height + kSeparatorLineHeight) : 0); 95} 96 97ExtensionInfoBar::~ExtensionInfoBar() { 98} 99 100void ExtensionInfoBar::Layout() { 101 InfoBarView::Layout(); 102 103 gfx::Size size = infobar_icon_->GetPreferredSize(); 104 infobar_icon_->SetBounds(StartX(), OffsetY(size), size.width(), 105 size.height()); 106 107 GetDelegate()->extension_view_host()->view()->SetBounds( 108 infobar_icon_->bounds().right() + kIconHorizontalMargin, 109 arrow_height(), 110 std::max(0, EndX() - StartX() - ContentMinimumWidth()), 111 height() - arrow_height() - 1); 112} 113 114void ExtensionInfoBar::ViewHierarchyChanged( 115 const ViewHierarchyChangedDetails& details) { 116 if (!details.is_add || (details.child != this) || (infobar_icon_ != NULL)) { 117 InfoBarView::ViewHierarchyChanged(details); 118 return; 119 } 120 121 extensions::ExtensionViewHost* extension_view_host = 122 GetDelegate()->extension_view_host(); 123 124 if (extension_view_host->extension()->ShowConfigureContextMenus()) { 125 icon_as_menu_ = new views::MenuButton(NULL, base::string16(), this, false); 126 icon_as_menu_->set_focusable(true); 127 infobar_icon_ = icon_as_menu_; 128 } else { 129 icon_as_image_ = new views::ImageView(); 130 infobar_icon_ = icon_as_image_; 131 } 132 133 // Wait until the icon image is loaded before showing it. 134 infobar_icon_->SetVisible(false); 135 AddChildView(infobar_icon_); 136 137 AddChildView(extension_view_host->view()); 138 139 // This must happen after adding all other children so InfoBarView can ensure 140 // the close button is the last child. 141 InfoBarView::ViewHierarchyChanged(details); 142 143 // This must happen after adding all children because it can trigger layout, 144 // which assumes that particular children (e.g. the close button) have already 145 // been added. 146 const extensions::Extension* extension = extension_view_host->extension(); 147 extension_misc::ExtensionIcons image_size = 148 extension_misc::EXTENSION_ICON_BITTY; 149 extensions::ExtensionResource icon_resource = 150 extensions::IconsInfo::GetIconResource( 151 extension, image_size, ExtensionIconSet::MATCH_EXACTLY); 152 extensions::ImageLoader* loader = 153 extensions::ImageLoader::Get(extension_view_host->browser_context()); 154 loader->LoadImageAsync( 155 extension, 156 icon_resource, 157 gfx::Size(image_size, image_size), 158 base::Bind(&ExtensionInfoBar::OnImageLoaded, 159 weak_ptr_factory_.GetWeakPtr())); 160} 161 162int ExtensionInfoBar::ContentMinimumWidth() const { 163 return infobar_icon_->GetPreferredSize().width() + kIconHorizontalMargin; 164 165} 166 167void ExtensionInfoBar::OnMenuButtonClicked(views::View* source, 168 const gfx::Point& point) { 169 if (!owner()) 170 return; // We're closing; don't call anything, it might access the owner. 171 const extensions::Extension* extension = 172 GetDelegate()->extension_view_host()->extension(); 173 DCHECK(icon_as_menu_); 174 175 scoped_refptr<ExtensionContextMenuModel> options_menu_contents = 176 new ExtensionContextMenuModel(extension, browser_); 177 DCHECK_EQ(icon_as_menu_, source); 178 RunMenuAt(options_menu_contents.get(), 179 icon_as_menu_, 180 views::MenuItemView::TOPLEFT); 181} 182 183void ExtensionInfoBar::OnImageLoaded(const gfx::Image& image) { 184 if (!GetDelegate()) 185 return; // The delegate can go away while we asynchronously load images. 186 187 const gfx::ImageSkia* icon = NULL; 188 // Fall back on the default extension icon on failure. 189 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 190 if (image.IsEmpty()) 191 icon = rb.GetImageNamed(IDR_EXTENSIONS_SECTION).ToImageSkia(); 192 else 193 icon = image.ToImageSkia(); 194 195 if (icon_as_menu_) { 196 const gfx::ImageSkia* drop_image = 197 rb.GetImageNamed(IDR_APP_DROPARROW).ToImageSkia(); 198 199 gfx::CanvasImageSource* source = new MenuImageSource(*icon, *drop_image); 200 gfx::ImageSkia menu_image = gfx::ImageSkia(source, source->size()); 201 icon_as_menu_->SetIcon(menu_image); 202 } else { 203 icon_as_image_->SetImage(*icon); 204 } 205 206 infobar_icon_->SetVisible(true); 207 208 Layout(); 209} 210 211ExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() { 212 return delegate()->AsExtensionInfoBarDelegate(); 213} 214