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