• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/platform_util.h"
11 #include "chrome/browser/ui/views/frame/browser_view.h"
12 #include "extensions/browser/image_loader.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/widget/widget.h"
27 
28 
29 // ExtensionInfoBarDelegate ----------------------------------------------------
30 
31 // static
CreateInfoBar(scoped_ptr<ExtensionInfoBarDelegate> delegate)32 scoped_ptr<infobars::InfoBar> ExtensionInfoBarDelegate::CreateInfoBar(
33     scoped_ptr<ExtensionInfoBarDelegate> delegate) {
34   Browser* browser = delegate->browser_;
35   return scoped_ptr<infobars::InfoBar>(
36       new ExtensionInfoBar(delegate.Pass(), browser));
37 }
38 
39 
40 // ExtensionInfoBar ------------------------------------------------------------
41 
42 namespace {
43 // The horizontal margin between the infobar icon and the Extension (HTML) view.
44 const int kIconHorizontalMargin = 1;
45 
46 class MenuImageSource: public gfx::CanvasImageSource {
47  public:
MenuImageSource(const gfx::ImageSkia & icon,const gfx::ImageSkia & drop_image)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 
~MenuImageSource()54   virtual ~MenuImageSource() {
55   }
56 
57   // Overridden from gfx::CanvasImageSource
Draw(gfx::Canvas * canvas)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:
ComputeSize(const gfx::ImageSkia & drop_image) const67   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 
ExtensionInfoBar(scoped_ptr<ExtensionInfoBarDelegate> delegate,Browser * browser)84 ExtensionInfoBar::ExtensionInfoBar(
85     scoped_ptr<ExtensionInfoBarDelegate> delegate,
86     Browser* browser)
87     : InfoBarView(delegate.PassAs<infobars::InfoBarDelegate>()),
88       browser_(browser),
89       infobar_icon_(NULL),
90       icon_as_menu_(NULL),
91       icon_as_image_(NULL),
92       weak_ptr_factory_(this) {
93 }
94 
~ExtensionInfoBar()95 ExtensionInfoBar::~ExtensionInfoBar() {
96 }
97 
Layout()98 void ExtensionInfoBar::Layout() {
99   InfoBarView::Layout();
100 
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
108   // here.
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))));
117 }
118 
ViewHierarchyChanged(const ViewHierarchyChangedDetails & details)119 void ExtensionInfoBar::ViewHierarchyChanged(
120     const ViewHierarchyChangedDetails& details) {
121   if (!details.is_add || (details.child != this) || (infobar_icon_ != NULL)) {
122     InfoBarView::ViewHierarchyChanged(details);
123     return;
124   }
125 
126   extensions::ExtensionViewHost* extension_view_host =
127       GetDelegate()->extension_view_host();
128 
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_;
133   } else {
134     icon_as_image_ = new views::ImageView();
135     infobar_icon_ = icon_as_image_;
136   }
137 
138   // Wait until the icon image is loaded before showing it.
139   infobar_icon_->SetVisible(false);
140   AddChildView(infobar_icon_);
141 
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());
148 
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);
152 
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
155   // been added.
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(
165       extension,
166       icon_resource,
167       gfx::Size(image_size, image_size),
168       base::Bind(&ExtensionInfoBar::OnImageLoaded,
169                  weak_ptr_factory_.GetWeakPtr()));
170 }
171 
ContentMinimumWidth() const172 int ExtensionInfoBar::ContentMinimumWidth() const {
173   return NonExtensionViewWidth() +
174       delegate()->AsExtensionInfoBarDelegate()->extension_view_host()->
175       view()->GetMinimumSize().width();
176 }
177 
OnMenuButtonClicked(views::View * source,const gfx::Point & point)178 void 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(
190       options_menu_contents.get(), icon_as_menu_, views::MENU_ANCHOR_TOPLEFT);
191 }
192 
OnImageLoaded(const gfx::Image & image)193 void ExtensionInfoBar::OnImageLoaded(const gfx::Image& image) {
194   if (!GetDelegate())
195     return;  // The delegate can go away while we asynchronously load images.
196 
197   const gfx::ImageSkia* icon = NULL;
198   // Fall back on the default extension icon on failure.
199   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
200   if (image.IsEmpty())
201     icon = rb.GetImageNamed(IDR_EXTENSIONS_SECTION).ToImageSkia();
202   else
203     icon = image.ToImageSkia();
204 
205   if (icon_as_menu_) {
206     const gfx::ImageSkia* drop_image =
207         rb.GetImageNamed(IDR_APP_DROPARROW).ToImageSkia();
208 
209     gfx::CanvasImageSource* source = new MenuImageSource(*icon, *drop_image);
210     gfx::ImageSkia menu_image = gfx::ImageSkia(source, source->size());
211     icon_as_menu_->SetImage(views::Button::STATE_NORMAL, menu_image);
212   } else {
213     icon_as_image_->SetImage(*icon);
214   }
215 
216   infobar_icon_->SizeToPreferredSize();
217   infobar_icon_->SetVisible(true);
218 
219   Layout();
220 }
221 
GetDelegate()222 ExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() {
223   return delegate()->AsExtensionInfoBarDelegate();
224 }
225 
GetDelegate() const226 const ExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() const {
227   return delegate()->AsExtensionInfoBarDelegate();
228 }
229 
NonExtensionViewWidth() const230 int ExtensionInfoBar::NonExtensionViewWidth() const {
231   return infobar_icon_->width() + kIconHorizontalMargin;
232 }
233