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