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.
5 #include "chrome/browser/ui/views/infobars/extension_infobar.h"
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"
30 // ExtensionInfoBarDelegate ----------------------------------------------------
33 scoped_ptr
<InfoBar
> ExtensionInfoBarDelegate::CreateInfoBar(
34 scoped_ptr
<ExtensionInfoBarDelegate
> delegate
) {
35 Browser
* browser
= delegate
->browser_
;
36 return scoped_ptr
<InfoBar
>(new ExtensionInfoBar(delegate
.Pass(), browser
));
40 // ExtensionInfoBar ------------------------------------------------------------
43 // The horizontal margin between the infobar icon and the Extension (HTML) view.
44 const int kIconHorizontalMargin
= 1;
46 class MenuImageSource
: public gfx::CanvasImageSource
{
48 MenuImageSource(const gfx::ImageSkia
& icon
, const gfx::ImageSkia
& drop_image
)
49 : gfx::CanvasImageSource(ComputeSize(drop_image
), false),
51 drop_image_(drop_image
) {
54 virtual ~MenuImageSource() {
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
,
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(),
73 // The margin between the extension icon and the drop-down arrow image.
74 static const int kDropArrowLeftMargin
= 3;
76 const gfx::ImageSkia icon_
;
77 const gfx::ImageSkia drop_image_
;
79 DISALLOW_COPY_AND_ASSIGN(MenuImageSource
);
84 ExtensionInfoBar::ExtensionInfoBar(
85 scoped_ptr
<ExtensionInfoBarDelegate
> delegate
,
87 : InfoBarView(delegate
.PassAs
<InfoBarDelegate
>()),
92 weak_ptr_factory_(this) {
95 ExtensionInfoBar::~ExtensionInfoBar() {
98 void ExtensionInfoBar::Layout() {
99 InfoBarView::Layout();
101 infobar_icon_
->SetPosition(gfx::Point(StartX(), OffsetY(infobar_icon_
)));
102 ExtensionViewViews
* extension_view
=
103 GetDelegate()->extension_view_host()->view();
104 // TODO(pkasting): We'd like to simply set the extension view's desired height
105 // at creation time and position using OffsetY() like for other infobar items,
106 // but the NativeViewHost inside does not seem to be clipped by the ClipRect()
107 // call in InfoBarView::PaintChildren(), so we have to manually clamp the size
109 extension_view
->SetSize(
110 gfx::Size(std::max(0, EndX() - StartX() - NonExtensionViewWidth()),
111 std::min(height() - kSeparatorLineHeight
- arrow_height(),
112 GetDelegate()->height())));
113 // We do SetPosition() separately after SetSize() so OffsetY() will work.
114 extension_view
->SetPosition(
115 gfx::Point(infobar_icon_
->bounds().right() + kIconHorizontalMargin
,
116 std::max(arrow_height(), OffsetY(extension_view
))));
119 void ExtensionInfoBar::ViewHierarchyChanged(
120 const ViewHierarchyChangedDetails
& details
) {
121 if (!details
.is_add
|| (details
.child
!= this) || (infobar_icon_
!= NULL
)) {
122 InfoBarView::ViewHierarchyChanged(details
);
126 extensions::ExtensionViewHost
* extension_view_host
=
127 GetDelegate()->extension_view_host();
129 if (extension_view_host
->extension()->ShowConfigureContextMenus()) {
130 icon_as_menu_
= new views::MenuButton(NULL
, base::string16(), this, false);
131 icon_as_menu_
->SetFocusable(true);
132 infobar_icon_
= icon_as_menu_
;
134 icon_as_image_
= new views::ImageView();
135 infobar_icon_
= icon_as_image_
;
138 // Wait until the icon image is loaded before showing it.
139 infobar_icon_
->SetVisible(false);
140 AddChildView(infobar_icon_
);
142 // Set the desired height of the ExtensionViewViews, so that when the
143 // AddChildView() call triggers InfoBarView::ViewHierarchyChanged(), it can
144 // read the correct height off this object in order to calculate the overall
145 // desired infobar height.
146 extension_view_host
->view()->SetSize(gfx::Size(0, GetDelegate()->height()));
147 AddChildView(extension_view_host
->view());
149 // This must happen after adding all other children so InfoBarView can ensure
150 // the close button is the last child.
151 InfoBarView::ViewHierarchyChanged(details
);
153 // This must happen after adding all children because it can trigger layout,
154 // which assumes that particular children (e.g. the close button) have already
156 const extensions::Extension
* extension
= extension_view_host
->extension();
157 extension_misc::ExtensionIcons image_size
=
158 extension_misc::EXTENSION_ICON_BITTY
;
159 extensions::ExtensionResource icon_resource
=
160 extensions::IconsInfo::GetIconResource(
161 extension
, image_size
, ExtensionIconSet::MATCH_EXACTLY
);
162 extensions::ImageLoader
* loader
=
163 extensions::ImageLoader::Get(extension_view_host
->browser_context());
164 loader
->LoadImageAsync(
167 gfx::Size(image_size
, image_size
),
168 base::Bind(&ExtensionInfoBar::OnImageLoaded
,
169 weak_ptr_factory_
.GetWeakPtr()));
172 int ExtensionInfoBar::ContentMinimumWidth() {
173 return NonExtensionViewWidth() +
174 GetDelegate()->extension_view_host()->view()->GetMinimumSize().width();
177 void ExtensionInfoBar::OnMenuButtonClicked(views::View
* source
,
178 const gfx::Point
& point
) {
180 return; // We're closing; don't call anything, it might access the owner.
181 const extensions::Extension
* extension
=
182 GetDelegate()->extension_view_host()->extension();
183 DCHECK(icon_as_menu_
);
185 scoped_refptr
<ExtensionContextMenuModel
> options_menu_contents
=
186 new ExtensionContextMenuModel(extension
, browser_
);
187 DCHECK_EQ(icon_as_menu_
, source
);
188 RunMenuAt(options_menu_contents
.get(),
190 views::MenuItemView::TOPLEFT
);
193 void ExtensionInfoBar::OnImageLoaded(const gfx::Image
& image
) {
195 return; // The delegate can go away while we asynchronously load images.
197 const gfx::ImageSkia
* icon
= NULL
;
198 // Fall back on the default extension icon on failure.
199 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
201 icon
= rb
.GetImageNamed(IDR_EXTENSIONS_SECTION
).ToImageSkia();
203 icon
= image
.ToImageSkia();
206 const gfx::ImageSkia
* drop_image
=
207 rb
.GetImageNamed(IDR_APP_DROPARROW
).ToImageSkia();
209 gfx::CanvasImageSource
* source
= new MenuImageSource(*icon
, *drop_image
);
210 gfx::ImageSkia menu_image
= gfx::ImageSkia(source
, source
->size());
211 icon_as_menu_
->SetIcon(menu_image
);
213 icon_as_image_
->SetImage(*icon
);
216 infobar_icon_
->SizeToPreferredSize();
217 infobar_icon_
->SetVisible(true);
222 ExtensionInfoBarDelegate
* ExtensionInfoBar::GetDelegate() {
223 return delegate()->AsExtensionInfoBarDelegate();
226 int ExtensionInfoBar::NonExtensionViewWidth() const {
227 return infobar_icon_
->width() + kIconHorizontalMargin
;