172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/infobars/extension_infobar.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_context_menu_model.h" 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_host.h" 972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/extensions/extension_infobar_delegate.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/platform_util.h" 1172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/infobars/infobar_background.h" 1272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/frame/browser_view.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h" 143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/common/extensions/extension_icon_set.h" 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_resource.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/theme_resources.h" 173f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/slide_animation.h" 1872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h" 1972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas_skia.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/button/menu_button.h" 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/menu/menu_2.h" 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget.h" 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// ExtensionInfoBarDelegate --------------------------------------------------- 2572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenInfoBar* ExtensionInfoBarDelegate::CreateInfoBar() { 2772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return new ExtensionInfoBar(this); 2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 3072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// ExtensionInfoBar ----------------------------------------------------------- 3172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 3272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsennamespace { 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The horizontal margin between the menu and the Extension (HTML) view. 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kMenuHorizontalMargin = 1; 3572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}; 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochExtensionInfoBar::ExtensionInfoBar(ExtensionInfoBarDelegate* delegate) 3872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen : InfoBarView(delegate), 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delegate_(delegate), 40dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen menu_(NULL), 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) { 4272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen delegate->set_observer(this); 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 4472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ExtensionView* extension_view = delegate->extension_host()->view(); 4572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen int height = extension_view->GetPreferredSize().height(); 46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SetBarTargetHeight((height > 0) ? (height + kSeparatorLineHeight) : 0); 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Get notified of resize events for the ExtensionView. 4972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen extension_view->SetContainer(this); 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochExtensionInfoBar::~ExtensionInfoBar() { 5372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (GetDelegate()) { 5472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen GetDelegate()->extension_host()->view()->SetContainer(NULL); 5572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen GetDelegate()->set_observer(NULL); 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionInfoBar::Layout() { 6072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen InfoBarView::Layout(); 6172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 6272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen gfx::Size menu_size = menu_->GetPreferredSize(); 63dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen menu_->SetBounds(StartX(), OffsetY(menu_size), menu_size.width(), 6472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen menu_size.height()); 6572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 66dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen GetDelegate()->extension_host()->view()->SetBounds( 67dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen menu_->bounds().right() + kMenuHorizontalMargin, 0, 68dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen std::max(0, EndX() - StartX() - ContentMinimumWidth()), height()); 69dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 70dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 71dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid ExtensionInfoBar::ViewHierarchyChanged(bool is_add, 72dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen View* parent, 73dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen View* child) { 74dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!is_add || (child != this) || (menu_ != NULL)) { 75dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen InfoBarView::ViewHierarchyChanged(is_add, parent, child); 76dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return; 77dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 78dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 79dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen menu_ = new views::MenuButton(NULL, std::wstring(), this, false); 80dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen menu_->SetVisible(false); 81dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen AddChildView(menu_); 82dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 83dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ExtensionHost* extension_host = GetDelegate()->extension_host(); 84dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen AddChildView(extension_host->view()); 85dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 86dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // This must happen after adding all other children so InfoBarView can ensure 87dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // the close button is the last child. 88dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen InfoBarView::ViewHierarchyChanged(is_add, parent, child); 89dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 90dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // This must happen after adding all children because it can trigger layout, 91dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // which assumes that particular children (e.g. the close button) have already 92dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // been added. 93dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const Extension* extension = extension_host->extension(); 94dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int image_size = Extension::EXTENSION_ICON_BITTY; 95dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ExtensionResource icon_resource = extension->GetIconResource( 96dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen image_size, ExtensionIconSet::MATCH_EXACTLY); 97dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!icon_resource.relative_path().empty()) { 98dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen tracker_.LoadImage(extension, icon_resource, 99dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen gfx::Size(image_size, image_size), ImageLoadingTracker::DONT_CACHE); 100dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } else { 101dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen OnImageLoaded(NULL, icon_resource, 0); 102dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 103dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 104dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 105dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenint ExtensionInfoBar::ContentMinimumWidth() const { 106dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return menu_->GetPreferredSize().width() + kMenuHorizontalMargin; 10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 10872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 10972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionInfoBar::OnExtensionMouseMove(ExtensionView* view) { 11072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 11172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 11272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionInfoBar::OnExtensionMouseLeave(ExtensionView* view) { 11372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 11472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionInfoBar::OnExtensionPreferredSizeChanged(ExtensionView* view) { 11672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ExtensionInfoBarDelegate* delegate = GetDelegate(); 11772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DCHECK_EQ(delegate->extension_host()->view(), view); 1183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // When the infobar is closed, it animates to 0 vertical height. We'll 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // continue to get size changed notifications from the ExtensionView, but we 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // need to ignore them otherwise we'll try to re-animate open (and leak the 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // infobar view). 12372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (delegate->closing()) 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 12672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen view->SetVisible(true); 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (height() == 0) 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch animation()->Reset(0.0); 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 13172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // Clamp height to a min and a max size of between 1 and 2 InfoBars. 132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SetBarTargetHeight(std::min(2 * kDefaultBarTargetHeight, 133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::max(kDefaultBarTargetHeight, view->GetPreferredSize().height()))); 13472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 13572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen animation()->Show(); 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 13872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionInfoBar::OnImageLoaded(SkBitmap* image, 139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const ExtensionResource& resource, 14072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen int index) { 14172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!GetDelegate()) 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // The delegate can go away while we asynchronously load images. 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 14472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen SkBitmap* icon = image; 14572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // Fall back on the default extension icon on failure. 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!image || image->empty()) 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch icon = rb.GetBitmapNamed(IDR_EXTENSIONS_SECTION); 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SkBitmap* drop_image = rb.GetBitmapNamed(IDR_APP_DROPARROW); 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int image_size = Extension::EXTENSION_ICON_BITTY; 15372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // The margin between the extension icon and the drop-down arrow bitmap. 15472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen static const int kDropArrowLeftMargin = 3; 15572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen scoped_ptr<gfx::CanvasSkia> canvas(new gfx::CanvasSkia( 15672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen image_size + kDropArrowLeftMargin + drop_image->width(), image_size, 15772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen false)); 15872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen canvas->DrawBitmapInt(*icon, 0, 0, icon->width(), icon->height(), 0, 0, 15972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen image_size, image_size, false); 16072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen canvas->DrawBitmapInt(*drop_image, image_size + kDropArrowLeftMargin, 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch image_size / 2); 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch menu_->SetIcon(canvas->ExtractBitmap()); 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch menu_->SetVisible(true); 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Layout(); 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionInfoBar::OnDelegateDeleted() { 16972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen GetDelegate()->extension_host()->view()->SetContainer(NULL); 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delegate_ = NULL; 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionInfoBar::RunMenu(View* source, const gfx::Point& pt) { 17472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const Extension* extension = GetDelegate()->extension_host()->extension(); 17572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!extension->ShowConfigureContextMenus()) 17672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return; 17772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!options_menu_contents_.get()) { 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser* browser = BrowserView::GetBrowserViewForNativeWindow( 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch platform_util::GetTopLevel(source->GetWidget()->GetNativeView()))-> 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browser(); 18272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen options_menu_contents_ = new ExtensionContextMenuModel(extension, browser, 18372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NULL); 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch options_menu_menu_.reset(new views::Menu2(options_menu_contents_.get())); 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch options_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPLEFT); 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 19072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() { 19172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return delegate_ ? delegate_->AsExtensionInfoBarDelegate() : NULL; 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 193