1// Copyright (c) 2011 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/platform_util.h" 11#include "chrome/browser/ui/views/infobars/infobar_background.h" 12#include "chrome/browser/ui/views/frame/browser_view.h" 13#include "chrome/common/extensions/extension.h" 14#include "chrome/common/extensions/extension_icon_set.h" 15#include "chrome/common/extensions/extension_resource.h" 16#include "grit/theme_resources.h" 17#include "ui/base/animation/slide_animation.h" 18#include "ui/base/resource/resource_bundle.h" 19#include "ui/gfx/canvas_skia.h" 20#include "views/controls/button/menu_button.h" 21#include "views/controls/menu/menu_2.h" 22#include "views/widget/widget.h" 23 24// ExtensionInfoBarDelegate --------------------------------------------------- 25 26InfoBar* ExtensionInfoBarDelegate::CreateInfoBar() { 27 return new ExtensionInfoBar(this); 28} 29 30// ExtensionInfoBar ----------------------------------------------------------- 31 32namespace { 33// The horizontal margin between the menu and the Extension (HTML) view. 34static const int kMenuHorizontalMargin = 1; 35}; 36 37ExtensionInfoBar::ExtensionInfoBar(ExtensionInfoBarDelegate* delegate) 38 : InfoBarView(delegate), 39 delegate_(delegate), 40 menu_(NULL), 41 ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) { 42 delegate->set_observer(this); 43 44 ExtensionView* extension_view = delegate->extension_host()->view(); 45 int height = extension_view->GetPreferredSize().height(); 46 SetBarTargetHeight((height > 0) ? (height + kSeparatorLineHeight) : 0); 47 48 // Get notified of resize events for the ExtensionView. 49 extension_view->SetContainer(this); 50} 51 52ExtensionInfoBar::~ExtensionInfoBar() { 53 if (GetDelegate()) { 54 GetDelegate()->extension_host()->view()->SetContainer(NULL); 55 GetDelegate()->set_observer(NULL); 56 } 57} 58 59void ExtensionInfoBar::Layout() { 60 InfoBarView::Layout(); 61 62 gfx::Size menu_size = menu_->GetPreferredSize(); 63 menu_->SetBounds(StartX(), OffsetY(menu_size), menu_size.width(), 64 menu_size.height()); 65 66 GetDelegate()->extension_host()->view()->SetBounds( 67 menu_->bounds().right() + kMenuHorizontalMargin, 0, 68 std::max(0, EndX() - StartX() - ContentMinimumWidth()), height()); 69} 70 71void ExtensionInfoBar::ViewHierarchyChanged(bool is_add, 72 View* parent, 73 View* child) { 74 if (!is_add || (child != this) || (menu_ != NULL)) { 75 InfoBarView::ViewHierarchyChanged(is_add, parent, child); 76 return; 77 } 78 79 menu_ = new views::MenuButton(NULL, std::wstring(), this, false); 80 menu_->SetVisible(false); 81 AddChildView(menu_); 82 83 ExtensionHost* extension_host = GetDelegate()->extension_host(); 84 AddChildView(extension_host->view()); 85 86 // This must happen after adding all other children so InfoBarView can ensure 87 // the close button is the last child. 88 InfoBarView::ViewHierarchyChanged(is_add, parent, child); 89 90 // This must happen after adding all children because it can trigger layout, 91 // which assumes that particular children (e.g. the close button) have already 92 // been added. 93 const Extension* extension = extension_host->extension(); 94 int image_size = Extension::EXTENSION_ICON_BITTY; 95 ExtensionResource icon_resource = extension->GetIconResource( 96 image_size, ExtensionIconSet::MATCH_EXACTLY); 97 if (!icon_resource.relative_path().empty()) { 98 tracker_.LoadImage(extension, icon_resource, 99 gfx::Size(image_size, image_size), ImageLoadingTracker::DONT_CACHE); 100 } else { 101 OnImageLoaded(NULL, icon_resource, 0); 102 } 103} 104 105int ExtensionInfoBar::ContentMinimumWidth() const { 106 return menu_->GetPreferredSize().width() + kMenuHorizontalMargin; 107} 108 109void ExtensionInfoBar::OnExtensionMouseMove(ExtensionView* view) { 110} 111 112void ExtensionInfoBar::OnExtensionMouseLeave(ExtensionView* view) { 113} 114 115void ExtensionInfoBar::OnExtensionPreferredSizeChanged(ExtensionView* view) { 116 ExtensionInfoBarDelegate* delegate = GetDelegate(); 117 DCHECK_EQ(delegate->extension_host()->view(), view); 118 119 // When the infobar is closed, it animates to 0 vertical height. We'll 120 // continue to get size changed notifications from the ExtensionView, but we 121 // need to ignore them otherwise we'll try to re-animate open (and leak the 122 // infobar view). 123 if (delegate->closing()) 124 return; 125 126 view->SetVisible(true); 127 128 if (height() == 0) 129 animation()->Reset(0.0); 130 131 // Clamp height to a min and a max size of between 1 and 2 InfoBars. 132 SetBarTargetHeight(std::min(2 * kDefaultBarTargetHeight, 133 std::max(kDefaultBarTargetHeight, view->GetPreferredSize().height()))); 134 135 animation()->Show(); 136} 137 138void ExtensionInfoBar::OnImageLoaded(SkBitmap* image, 139 const ExtensionResource& resource, 140 int index) { 141 if (!GetDelegate()) 142 return; // The delegate can go away while we asynchronously load images. 143 144 SkBitmap* icon = image; 145 // Fall back on the default extension icon on failure. 146 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 147 if (!image || image->empty()) 148 icon = rb.GetBitmapNamed(IDR_EXTENSIONS_SECTION); 149 150 SkBitmap* drop_image = rb.GetBitmapNamed(IDR_APP_DROPARROW); 151 152 int image_size = Extension::EXTENSION_ICON_BITTY; 153 // The margin between the extension icon and the drop-down arrow bitmap. 154 static const int kDropArrowLeftMargin = 3; 155 scoped_ptr<gfx::CanvasSkia> canvas(new gfx::CanvasSkia( 156 image_size + kDropArrowLeftMargin + drop_image->width(), image_size, 157 false)); 158 canvas->DrawBitmapInt(*icon, 0, 0, icon->width(), icon->height(), 0, 0, 159 image_size, image_size, false); 160 canvas->DrawBitmapInt(*drop_image, image_size + kDropArrowLeftMargin, 161 image_size / 2); 162 menu_->SetIcon(canvas->ExtractBitmap()); 163 menu_->SetVisible(true); 164 165 Layout(); 166} 167 168void ExtensionInfoBar::OnDelegateDeleted() { 169 GetDelegate()->extension_host()->view()->SetContainer(NULL); 170 delegate_ = NULL; 171} 172 173void ExtensionInfoBar::RunMenu(View* source, const gfx::Point& pt) { 174 const Extension* extension = GetDelegate()->extension_host()->extension(); 175 if (!extension->ShowConfigureContextMenus()) 176 return; 177 178 if (!options_menu_contents_.get()) { 179 Browser* browser = BrowserView::GetBrowserViewForNativeWindow( 180 platform_util::GetTopLevel(source->GetWidget()->GetNativeView()))-> 181 browser(); 182 options_menu_contents_ = new ExtensionContextMenuModel(extension, browser, 183 NULL); 184 } 185 186 options_menu_menu_.reset(new views::Menu2(options_menu_contents_.get())); 187 options_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPLEFT); 188} 189 190ExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() { 191 return delegate_ ? delegate_->AsExtensionInfoBarDelegate() : NULL; 192} 193