Home | History | Annotate | Download | only in infobars
      1 // Copyright (c) 2011 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/platform_util.h"
     11 #include "chrome/browser/ui/views/infobars/infobar_background.h"
     12 #include "chrome/browser/ui/views/frame/browser_view.h"
     13 #include "chrome/common/extensions/extension.h"
     14 #include "chrome/common/extensions/extension_icon_set.h"
     15 #include "chrome/common/extensions/extension_resource.h"
     16 #include "grit/theme_resources.h"
     17 #include "ui/base/animation/slide_animation.h"
     18 #include "ui/base/resource/resource_bundle.h"
     19 #include "ui/gfx/canvas_skia.h"
     20 #include "views/controls/button/menu_button.h"
     21 #include "views/controls/menu/menu_2.h"
     22 #include "views/widget/widget.h"
     23 
     24 // ExtensionInfoBarDelegate ---------------------------------------------------
     25 
     26 InfoBar* ExtensionInfoBarDelegate::CreateInfoBar() {
     27   return new ExtensionInfoBar(this);
     28 }
     29 
     30 // ExtensionInfoBar -----------------------------------------------------------
     31 
     32 namespace {
     33 // The horizontal margin between the menu and the Extension (HTML) view.
     34 static const int kMenuHorizontalMargin = 1;
     35 };
     36 
     37 ExtensionInfoBar::ExtensionInfoBar(ExtensionInfoBarDelegate* delegate)
     38     : InfoBarView(delegate),
     39       delegate_(delegate),
     40       menu_(NULL),
     41       ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) {
     42   delegate->set_observer(this);
     43 
     44   ExtensionView* extension_view = delegate->extension_host()->view();
     45   int height = extension_view->GetPreferredSize().height();
     46   SetBarTargetHeight((height > 0) ? (height + kSeparatorLineHeight) : 0);
     47 
     48   // Get notified of resize events for the ExtensionView.
     49   extension_view->SetContainer(this);
     50 }
     51 
     52 ExtensionInfoBar::~ExtensionInfoBar() {
     53   if (GetDelegate()) {
     54     GetDelegate()->extension_host()->view()->SetContainer(NULL);
     55     GetDelegate()->set_observer(NULL);
     56   }
     57 }
     58 
     59 void ExtensionInfoBar::Layout() {
     60   InfoBarView::Layout();
     61 
     62   gfx::Size menu_size = menu_->GetPreferredSize();
     63   menu_->SetBounds(StartX(), OffsetY(menu_size), menu_size.width(),
     64                    menu_size.height());
     65 
     66   GetDelegate()->extension_host()->view()->SetBounds(
     67       menu_->bounds().right() + kMenuHorizontalMargin, 0,
     68       std::max(0, EndX() - StartX() - ContentMinimumWidth()), height());
     69 }
     70 
     71 void ExtensionInfoBar::ViewHierarchyChanged(bool is_add,
     72                                             View* parent,
     73                                             View* child) {
     74   if (!is_add || (child != this) || (menu_ != NULL)) {
     75     InfoBarView::ViewHierarchyChanged(is_add, parent, child);
     76     return;
     77   }
     78 
     79   menu_ = new views::MenuButton(NULL, std::wstring(), this, false);
     80   menu_->SetVisible(false);
     81   AddChildView(menu_);
     82 
     83   ExtensionHost* extension_host = GetDelegate()->extension_host();
     84   AddChildView(extension_host->view());
     85 
     86   // This must happen after adding all other children so InfoBarView can ensure
     87   // the close button is the last child.
     88   InfoBarView::ViewHierarchyChanged(is_add, parent, child);
     89 
     90   // This must happen after adding all children because it can trigger layout,
     91   // which assumes that particular children (e.g. the close button) have already
     92   // been added.
     93   const Extension* extension = extension_host->extension();
     94   int image_size = Extension::EXTENSION_ICON_BITTY;
     95   ExtensionResource icon_resource = extension->GetIconResource(
     96       image_size, ExtensionIconSet::MATCH_EXACTLY);
     97   if (!icon_resource.relative_path().empty()) {
     98     tracker_.LoadImage(extension, icon_resource,
     99         gfx::Size(image_size, image_size), ImageLoadingTracker::DONT_CACHE);
    100   } else {
    101     OnImageLoaded(NULL, icon_resource, 0);
    102   }
    103 }
    104 
    105 int ExtensionInfoBar::ContentMinimumWidth() const {
    106   return menu_->GetPreferredSize().width() + kMenuHorizontalMargin;
    107 }
    108 
    109 void ExtensionInfoBar::OnExtensionMouseMove(ExtensionView* view) {
    110 }
    111 
    112 void ExtensionInfoBar::OnExtensionMouseLeave(ExtensionView* view) {
    113 }
    114 
    115 void ExtensionInfoBar::OnExtensionPreferredSizeChanged(ExtensionView* view) {
    116   ExtensionInfoBarDelegate* delegate = GetDelegate();
    117   DCHECK_EQ(delegate->extension_host()->view(), view);
    118 
    119   // When the infobar is closed, it animates to 0 vertical height. We'll
    120   // continue to get size changed notifications from the ExtensionView, but we
    121   // need to ignore them otherwise we'll try to re-animate open (and leak the
    122   // infobar view).
    123   if (delegate->closing())
    124     return;
    125 
    126   view->SetVisible(true);
    127 
    128   if (height() == 0)
    129     animation()->Reset(0.0);
    130 
    131   // Clamp height to a min and a max size of between 1 and 2 InfoBars.
    132   SetBarTargetHeight(std::min(2 * kDefaultBarTargetHeight,
    133       std::max(kDefaultBarTargetHeight, view->GetPreferredSize().height())));
    134 
    135   animation()->Show();
    136 }
    137 
    138 void ExtensionInfoBar::OnImageLoaded(SkBitmap* image,
    139                                      const ExtensionResource& resource,
    140                                      int index) {
    141   if (!GetDelegate())
    142     return;  // The delegate can go away while we asynchronously load images.
    143 
    144   SkBitmap* icon = image;
    145   // Fall back on the default extension icon on failure.
    146   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    147   if (!image || image->empty())
    148     icon = rb.GetBitmapNamed(IDR_EXTENSIONS_SECTION);
    149 
    150   SkBitmap* drop_image = rb.GetBitmapNamed(IDR_APP_DROPARROW);
    151 
    152   int image_size = Extension::EXTENSION_ICON_BITTY;
    153   // The margin between the extension icon and the drop-down arrow bitmap.
    154   static const int kDropArrowLeftMargin = 3;
    155   scoped_ptr<gfx::CanvasSkia> canvas(new gfx::CanvasSkia(
    156       image_size + kDropArrowLeftMargin + drop_image->width(), image_size,
    157       false));
    158   canvas->DrawBitmapInt(*icon, 0, 0, icon->width(), icon->height(), 0, 0,
    159                         image_size, image_size, false);
    160   canvas->DrawBitmapInt(*drop_image, image_size + kDropArrowLeftMargin,
    161                         image_size / 2);
    162   menu_->SetIcon(canvas->ExtractBitmap());
    163   menu_->SetVisible(true);
    164 
    165   Layout();
    166 }
    167 
    168 void ExtensionInfoBar::OnDelegateDeleted() {
    169   GetDelegate()->extension_host()->view()->SetContainer(NULL);
    170   delegate_ = NULL;
    171 }
    172 
    173 void ExtensionInfoBar::RunMenu(View* source, const gfx::Point& pt) {
    174   const Extension* extension = GetDelegate()->extension_host()->extension();
    175   if (!extension->ShowConfigureContextMenus())
    176     return;
    177 
    178   if (!options_menu_contents_.get()) {
    179     Browser* browser = BrowserView::GetBrowserViewForNativeWindow(
    180         platform_util::GetTopLevel(source->GetWidget()->GetNativeView()))->
    181             browser();
    182     options_menu_contents_ = new ExtensionContextMenuModel(extension, browser,
    183                                                            NULL);
    184   }
    185 
    186   options_menu_menu_.reset(new views::Menu2(options_menu_contents_.get()));
    187   options_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPLEFT);
    188 }
    189 
    190 ExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() {
    191   return delegate_ ? delegate_->AsExtensionInfoBarDelegate() : NULL;
    192 }
    193