extension_infobar.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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/animation/slide_animation.h"
20#include "ui/base/resource/resource_bundle.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