Home | History | Annotate | Download | only in infobars
      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
     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:
     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 
     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 
     95 ExtensionInfoBar::~ExtensionInfoBar() {
     96 }
     97 
     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 
    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 
    172 int ExtensionInfoBar::ContentMinimumWidth() const {
    173   return NonExtensionViewWidth() +
    174       delegate()->AsExtensionInfoBarDelegate()->extension_view_host()->
    175       view()->GetMinimumSize().width();
    176 }
    177 
    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 
    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 
    222 ExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() {
    223   return delegate()->AsExtensionInfoBarDelegate();
    224 }
    225 
    226 const ExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() const {
    227   return delegate()->AsExtensionInfoBarDelegate();
    228 }
    229 
    230 int ExtensionInfoBar::NonExtensionViewWidth() const {
    231   return infobar_icon_->width() + kIconHorizontalMargin;
    232 }
    233