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_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 
     32 InfoBar* ExtensionInfoBarDelegate::CreateInfoBar(InfoBarService* owner) {
     33   return new ExtensionInfoBar(owner, this, browser_);
     34 }
     35 
     36 
     37 // ExtensionInfoBar ------------------------------------------------------------
     38 
     39 namespace {
     40 // The horizontal margin between the infobar icon and the Extension (HTML) view.
     41 const int kIconHorizontalMargin = 1;
     42 
     43 class 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 
     81 ExtensionInfoBar::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 
     97 ExtensionInfoBar::~ExtensionInfoBar() {
     98   if (GetDelegate())
     99     GetDelegate()->set_observer(NULL);
    100 }
    101 
    102 void 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 
    116 void 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 
    163 int ExtensionInfoBar::ContentMinimumWidth() const {
    164   return infobar_icon_->GetPreferredSize().width() + kIconHorizontalMargin;
    165 
    166 }
    167 
    168 void ExtensionInfoBar::OnDelegateDeleted() {
    169   delegate_ = NULL;
    170 }
    171 
    172 void 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 
    188 void 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 
    216 ExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() {
    217   return delegate_ ? delegate_->AsExtensionInfoBarDelegate() : NULL;
    218 }
    219