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