summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views')
-rw-r--r--chrome/browser/views/bookmark_bar_view.cc3
-rw-r--r--chrome/browser/views/extensions/extension_shelf.cc589
-rw-r--r--chrome/browser/views/extensions/extension_shelf.h117
-rw-r--r--chrome/browser/views/extensions/extension_view.cc155
-rw-r--r--chrome/browser/views/extensions/extension_view.h109
5 files changed, 970 insertions, 3 deletions
diff --git a/chrome/browser/views/bookmark_bar_view.cc b/chrome/browser/views/bookmark_bar_view.cc
index d26165c1..adbd864 100644
--- a/chrome/browser/views/bookmark_bar_view.cc
+++ b/chrome/browser/views/bookmark_bar_view.cc
@@ -16,8 +16,6 @@
#include "chrome/browser/bookmarks/bookmark_utils.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_theme_provider.h"
-#include "chrome/browser/extensions/extension_view.h"
-#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
@@ -28,7 +26,6 @@
#include "chrome/browser/views/event_utils.h"
#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/browser/views/location_bar_view.h"
-#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/page_transition_types.h"
#include "chrome/common/pref_names.h"
diff --git a/chrome/browser/views/extensions/extension_shelf.cc b/chrome/browser/views/extensions/extension_shelf.cc
new file mode 100644
index 0000000..19e15b4
--- /dev/null
+++ b/chrome/browser/views/extensions/extension_shelf.cc
@@ -0,0 +1,589 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/views/extensions/extension_shelf.h"
+
+#include <algorithm>
+
+#include "app/resource_bundle.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/extensions/extension_process_manager.h"
+#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/views/extensions/extension_view.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/notification_service.h"
+#include "skia/ext/skia_utils.h"
+#include "views/controls/label.h"
+#include "views/screen.h"
+#include "views/widget/root_view.h"
+
+namespace {
+
+// Margins around the content.
+static const int kTopMargin = 2;
+static const int kBottomMargin = 2;
+static const int kLeftMargin = 0;
+static const int kRightMargin = 0;
+
+// Padding on left and right side of an extension toolstrip.
+static const int kToolstripPadding = 2;
+
+// Width of the toolstrip divider.
+static const int kToolstripDividerWidth = 2;
+
+// Preferred height of the ExtensionShelf.
+static const int kShelfHeight = 29;
+
+// Colors for the ExtensionShelf.
+static const SkColor kBackgroundColor = SkColorSetRGB(230, 237, 244);
+static const SkColor kBorderColor = SkColorSetRGB(201, 212, 225);
+static const SkColor kDividerHighlightColor = SkColorSetRGB(247, 250, 253);
+
+// Text colors for the handle
+static const SkColor kHandleTextColor = SkColorSetRGB(6, 45, 117);
+static const SkColor kHandleTextHighlightColor =
+ SkColorSetARGB(200, 255, 255, 255);
+
+// Handle padding
+static const int kHandlePadding = 4;
+
+// TODO(erikkay) convert back to a gradient when Glen figures out the
+// specs.
+// static const SkColor kBackgroundColor = SkColorSetRGB(237, 244, 252);
+// static const SkColor kTopGradientColor = SkColorSetRGB(222, 234, 248);
+
+// Delays for showing and hiding the shelf handle.
+static const int kHideDelayMs = 500;
+
+} // namespace
+
+
+// A small handle that is used for dragging or otherwise interacting with an
+// extension toolstrip.
+class ExtensionShelfHandle : public views::View {
+ public:
+ explicit ExtensionShelfHandle(ExtensionShelf* shelf);
+
+ // The ExtensionView that the handle is attached to.
+ void SetExtensionView(ExtensionView* v);
+
+ // View
+ virtual void Paint(gfx::Canvas* canvas);
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+ virtual void OnMouseEntered(const views::MouseEvent& event);
+ virtual void OnMouseExited(const views::MouseEvent& event);
+ virtual bool OnMousePressed(const views::MouseEvent& event);
+ virtual bool OnMouseDragged(const views::MouseEvent& event);
+ virtual void OnMouseReleased(const views::MouseEvent& event, bool canceled);
+ virtual bool IsFocusable() const { return true; }
+
+ private:
+ ExtensionShelf* shelf_;
+ ExtensionView* extension_view_;
+ scoped_ptr<views::Label> title_;
+ bool dragging_;
+ gfx::Point initial_drag_location_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionShelfHandle);
+};
+
+ExtensionShelfHandle::ExtensionShelfHandle(ExtensionShelf* shelf)
+ : shelf_(shelf), extension_view_(NULL), dragging_(false) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ // |title_| isn't actually put in the view hierarchy. We just use it
+ // to draw in place. The reason for this is so that we can properly handle
+ // the various mouse events necessary for hovering and dragging.
+ title_.reset(new views::Label(L"", rb.GetFont(ResourceBundle::BaseFont)));
+ title_->SetColor(kHandleTextColor);
+ title_->SetDrawHighlighted(true);
+ title_->SetHighlightColor(kHandleTextHighlightColor);
+ title_->SetBounds(kHandlePadding, kHandlePadding, 100, 100);
+ title_->SizeToPreferredSize();
+}
+
+void ExtensionShelfHandle::SetExtensionView(ExtensionView* v) {
+ DCHECK(v->extension());
+ extension_view_ = v;
+ if (!extension_view_->extension())
+ return;
+ title_->SetText(UTF8ToWide(extension_view_->extension()->name()));
+ title_->SizeToPreferredSize();
+ SizeToPreferredSize();
+}
+
+void ExtensionShelfHandle::Paint(gfx::Canvas* canvas) {
+ canvas->FillRectInt(kBackgroundColor, 0, 0, width(), height());
+ canvas->FillRectInt(kBorderColor, 0, 0, width(), 1);
+ canvas->FillRectInt(kBorderColor, 0, 0, 1, height() - 1);
+ canvas->FillRectInt(kBorderColor, width() - 1, 0, 1, height() - 1);
+ int ext_width = extension_view_->width() + kToolstripPadding +
+ kToolstripDividerWidth;
+ if (ext_width < width()) {
+ canvas->FillRectInt(kBorderColor, ext_width, height() - 1,
+ width() - ext_width, 1);
+ }
+
+ // Draw the title using a Label as a stamp.
+ // See constructor for comment about this.
+ title_->ProcessPaint(canvas);
+
+ if (dragging_) {
+ // when we're dragging, draw the bottom border.
+ canvas->FillRectInt(kBorderColor, 0, height() - 1, width(), 1);
+ }
+}
+
+gfx::Size ExtensionShelfHandle::GetPreferredSize() {
+ gfx::Size sz = title_->GetPreferredSize();
+ if (extension_view_) {
+ int width = std::max(extension_view_->width() + 2, sz.width());
+ sz.set_width(width);
+ }
+ sz.Enlarge(kHandlePadding * 2, kHandlePadding * 2);
+ if (dragging_) {
+ gfx::Size extension_size = extension_view_->GetPreferredSize();
+ sz.Enlarge(0, extension_size.height() + 2);
+ }
+ return sz;
+}
+
+void ExtensionShelfHandle::Layout() {
+ if (dragging_) {
+ int y = title_->bounds().bottom() + kHandlePadding + 1;
+ extension_view_->SetBounds(1,
+ y,
+ extension_view_->width(),
+ extension_view_->height());
+ }
+}
+
+void ExtensionShelfHandle::OnMouseEntered(const views::MouseEvent& event) {
+ DCHECK(extension_view_);
+ shelf_->OnExtensionMouseEvent(extension_view_);
+}
+
+void ExtensionShelfHandle::OnMouseExited(const views::MouseEvent& event) {
+ DCHECK(extension_view_);
+ shelf_->OnExtensionMouseLeave(extension_view_);
+}
+
+bool ExtensionShelfHandle::OnMousePressed(const views::MouseEvent& event) {
+ initial_drag_location_ = event.location();
+ return true;
+}
+
+bool ExtensionShelfHandle::OnMouseDragged(const views::MouseEvent& event) {
+ if (!dragging_) {
+ int y_delta = abs(initial_drag_location_.y() - event.location().y());
+ int x_delta = abs(initial_drag_location_.x() - event.location().x());
+ if (y_delta > GetVerticalDragThreshold() ||
+ x_delta > GetHorizontalDragThreshold()) {
+ dragging_ = true;
+ shelf_->DragExtension();
+ }
+ } else {
+ // When freely dragging a window, you can really only trust the
+ // actual screen point. Coordinate conversions, just don't work.
+ gfx::Point screen = views::Screen::GetCursorScreenPoint();
+
+ // However, the handle is actually a child of the browser window
+ // so we need to convert it back to local coordinates.
+ gfx::Point origin(0, 0);
+ views::View::ConvertPointToScreen(shelf_->GetRootView(), &origin);
+ screen.set_x(screen.x() - origin.x() - initial_drag_location_.x());
+ screen.set_y(screen.y() - origin.y() - initial_drag_location_.y());
+ shelf_->DragHandleTo(screen);
+ }
+ return true;
+}
+
+void ExtensionShelfHandle::OnMouseReleased(const views::MouseEvent& event,
+ bool canceled) {
+ if (dragging_) {
+ views::View::OnMouseReleased(event, canceled);
+ dragging_ = false;
+ // |this| and |shelf_| are in different view hierarchies, so we need to
+ // convert to screen coordinates and back again to map locations.
+ gfx::Point loc = event.location();
+ View::ConvertPointToScreen(this, &loc);
+ View::ConvertPointToView(NULL, shelf_, &loc);
+ shelf_->DropExtension(loc, canceled);
+ }
+}
+
+////////////////////////////////////////////////
+
+ExtensionShelf::ExtensionShelf(Browser* browser)
+ : handle_(NULL),
+ handle_visible_(false),
+ current_toolstrip_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(timer_factory_(this)),
+ drag_placeholder_view_(NULL),
+ model_(new ExtensionShelfModel(browser)) {
+ model_->AddObserver(this);
+ LoadFromModel();
+}
+
+ExtensionShelf::~ExtensionShelf() {
+ model_->RemoveObserver(this);
+}
+
+BrowserBubble* ExtensionShelf::GetHandle() {
+ if (!handle_.get() && current_toolstrip_) {
+ ExtensionShelfHandle* handle_view = new ExtensionShelfHandle(this);
+ handle_view->SetExtensionView(current_toolstrip_->view());
+ handle_.reset(new BrowserBubble(handle_view, GetWidget(),
+ gfx::Point(0, 0)));
+ handle_->set_delegate(this);
+ }
+ return handle_.get();
+}
+
+void ExtensionShelf::Paint(gfx::Canvas* canvas) {
+#if 0
+ // TODO(erikkay) re-enable this when Glen has the gradient values worked out.
+ SkPaint paint;
+ paint.setShader(skia::CreateGradientShader(0,
+ height(),
+ kTopGradientColor,
+ kBackgroundColor))->safeUnref();
+ canvas->FillRectInt(0, 0, width(), height(), paint);
+#else
+ canvas->FillRectInt(kBackgroundColor, 0, 0, width(), height());
+#endif
+
+ canvas->FillRectInt(kBorderColor, 0, 0, width(), 1);
+ canvas->FillRectInt(kBorderColor, 0, height() - 1, width(), 1);
+
+ int count = GetChildViewCount();
+ for (int i = 0; i < count; ++i) {
+ int right = GetChildViewAt(i)->bounds().right() + kToolstripPadding;
+ int h = height() - 2;
+ canvas->FillRectInt(kBorderColor, right, 1, 1, h);
+ canvas->FillRectInt(kDividerHighlightColor, right + 1, 1, 1, h);
+ }
+
+ SkRect background_rect = {
+ SkIntToScalar(0),
+ SkIntToScalar(1),
+ SkIntToScalar(1),
+ SkIntToScalar(height() - 2)};
+ InitBackground(canvas, background_rect);
+}
+
+gfx::Size ExtensionShelf::GetPreferredSize() {
+ if (model_->count())
+ return gfx::Size(0, kShelfHeight);
+ return gfx::Size(0, 0);
+}
+
+void ExtensionShelf::ChildPreferredSizeChanged(View* child) {
+ Layout();
+}
+
+void ExtensionShelf::Layout() {
+ if (!GetParent())
+ return;
+
+ int x = kLeftMargin;
+ int y = kTopMargin;
+ int content_height = height() - kTopMargin - kBottomMargin;
+ int max_x = width() - kRightMargin;
+
+ int count = model_->count();
+ for (int i = 0; i < count; ++i) {
+ x += kToolstripPadding; // left padding
+ ExtensionHost* toolstrip = model_->ToolstripAt(i);
+ ExtensionView* extension_view = toolstrip->view();
+ gfx::Size pref = extension_view->GetPreferredSize();
+ int next_x = x + pref.width() + kToolstripPadding; // right padding
+ extension_view->set_is_clipped(next_x >= max_x);
+ extension_view->SetBounds(x, y, pref.width(), content_height);
+ extension_view->Layout();
+ x = next_x + kToolstripDividerWidth;
+ }
+ if (handle_.get())
+ LayoutShelfHandle();
+ SchedulePaint();
+}
+
+void ExtensionShelf::OnMouseEntered(const views::MouseEvent& event) {
+ ExtensionHost* toolstrip = ToolstripAtX(event.x());
+ if (toolstrip) {
+ current_toolstrip_ = toolstrip;
+ ShowShelfHandle();
+ }
+}
+
+void ExtensionShelf::OnMouseExited(const views::MouseEvent& event) {
+ HideShelfHandle(kHideDelayMs);
+}
+
+void ExtensionShelf::ToolstripInsertedAt(ExtensionHost* toolstrip,
+ int index) {
+ bool had_views = GetChildViewCount() > 0;
+ ExtensionView* view = toolstrip->view();
+ if (!background_.empty())
+ view->SetBackground(background_);
+ AddChildView(view);
+ view->SetContainer(this);
+ if (!had_views)
+ PreferredSizeChanged();
+ Layout();
+}
+
+void ExtensionShelf::ToolstripRemovingAt(ExtensionHost* toolstrip,
+ int index) {
+ ExtensionView* view = toolstrip->view();
+ RemoveChildView(view);
+ Layout();
+}
+
+void ExtensionShelf::ToolstripDraggingFrom(ExtensionHost* toolstrip,
+ int index) {
+}
+
+void ExtensionShelf::ToolstripMoved(ExtensionHost* toolstrip,
+ int from_index,
+ int to_index) {
+ Layout();
+}
+
+void ExtensionShelf::ToolstripChangedAt(ExtensionHost* toolstrip,
+ int index) {
+}
+
+void ExtensionShelf::ExtensionShelfEmpty() {
+ PreferredSizeChanged();
+}
+
+void ExtensionShelf::ShelfModelReloaded() {
+ // None of the child views are parent owned, so nothing is being leaked here.
+ RemoveAllChildViews(false);
+ LoadFromModel();
+}
+
+void ExtensionShelf::OnExtensionMouseEvent(ExtensionView* view) {
+ // Ignore these events when dragging.
+ if (drag_placeholder_view_)
+ return;
+ ExtensionHost *toolstrip = ToolstripForView(view);
+ if (toolstrip != current_toolstrip_)
+ current_toolstrip_ = toolstrip;
+ if (current_toolstrip_)
+ ShowShelfHandle();
+}
+
+void ExtensionShelf::OnExtensionMouseLeave(ExtensionView* view) {
+ // Ignore these events when dragging.
+ if (drag_placeholder_view_)
+ return;
+ ExtensionHost *toolstrip = ToolstripForView(view);
+ if (toolstrip == current_toolstrip_)
+ HideShelfHandle(kHideDelayMs);
+}
+
+void ExtensionShelf::BubbleBrowserWindowMoved(BrowserBubble* bubble) {
+ HideShelfHandle(0);
+}
+
+void ExtensionShelf::BubbleBrowserWindowClosed(BrowserBubble* bubble) {
+ // We'll be going away shortly, so no need to do any other teardown here.
+ HideShelfHandle(0);
+}
+
+void ExtensionShelf::DragExtension() {
+ // Construct a placeholder view to replace the view.
+ // TODO(erikkay) the placeholder should draw a dimmed version of the
+ // extension view
+ drag_placeholder_view_ = new View();
+ drag_placeholder_view_->SetBounds(current_toolstrip_->view()->bounds());
+ AddChildView(drag_placeholder_view_);
+
+ // Now move the view into the handle's widget.
+ ExtensionShelfHandle* handle_view =
+ static_cast<ExtensionShelfHandle*>(GetHandle()->view());
+ handle_view->AddChildView(current_toolstrip_->view());
+ handle_view->SizeToPreferredSize();
+ handle_->ResizeToView();
+ handle_view->Layout();
+ handle_->DetachFromBrowser();
+ SchedulePaint();
+}
+
+void ExtensionShelf::DropExtension(const gfx::Point& pt, bool cancel) {
+ handle_->AttachToBrowser();
+
+ // Replace the placeholder view with the original.
+ AddChildView(current_toolstrip_->view());
+ current_toolstrip_->view()->SetBounds(drag_placeholder_view_->bounds());
+ RemoveChildView(drag_placeholder_view_);
+ delete drag_placeholder_view_;
+ drag_placeholder_view_ = NULL;
+
+ ExtensionHost* toolstrip = ToolstripAtX(pt.x());
+ if (toolstrip) {
+ int from = model_->IndexOfToolstrip(current_toolstrip_);
+ int to = model_->IndexOfToolstrip(toolstrip);
+ model_->MoveToolstripAt(from, to);
+ }
+
+ ExtensionShelfHandle* handle_view =
+ static_cast<ExtensionShelfHandle*>(GetHandle()->view());
+ handle_view->SizeToPreferredSize();
+ handle_view->Layout();
+ handle_->ResizeToView();
+ LayoutShelfHandle();
+ SchedulePaint();
+}
+
+void ExtensionShelf::DragHandleTo(const gfx::Point& pt) {
+ handle_->MoveTo(pt.x(), pt.y());
+ // TODO(erikkay) as this gets dragged around, update the placeholder view
+ // on the shelf to show where it will get dropped to.
+}
+
+void ExtensionShelf::InitBackground(gfx::Canvas* canvas,
+ const SkRect& subset) {
+ if (!background_.empty())
+ return;
+
+ const SkBitmap& background = canvas->getDevice()->accessBitmap(false);
+
+ // Extract the correct subset of the toolstrip background into a bitmap. We
+ // must use a temporary here because extractSubset() returns a bitmap that
+ // references pixels in the original one and we want to actually make a copy
+ // that will have a long lifetime.
+ SkBitmap temp;
+ temp.setConfig(background.config(),
+ static_cast<int>(subset.width()),
+ static_cast<int>(subset.height()));
+
+ SkRect mapped_subset = subset;
+ bool result = canvas->getTotalMatrix().mapRect(&mapped_subset);
+ DCHECK(result);
+
+ SkIRect isubset;
+ mapped_subset.round(&isubset);
+ result = background.extractSubset(&temp, isubset);
+ if (!result)
+ return;
+
+ temp.copyTo(&background_, temp.config());
+ DCHECK(background_.readyToDraw());
+
+ // Tell all extension views about the new background
+ int count = model_->count();
+ for (int i = 0; i < count; ++i)
+ model_->ToolstripAt(i)->view()->SetBackground(background_);
+}
+
+ExtensionHost* ExtensionShelf::ToolstripAtX(int x) {
+ int count = model_->count();
+ if (count == 0)
+ return NULL;
+
+ if (x < 0)
+ return model_->ToolstripAt(0);
+
+ for (int i = 0; i < count; ++i) {
+ ExtensionHost* toolstrip = model_->ToolstripAt(i);
+ ExtensionView* view = toolstrip->view();
+ if (x > (view->x() + view->width() + kToolstripPadding))
+ continue;
+ return toolstrip;
+ }
+
+ return model_->ToolstripAt(count - 1);
+}
+
+ExtensionHost* ExtensionShelf::ToolstripForView(ExtensionView* view) {
+ int count = model_->count();
+ for (int i = 0; i < count; ++i) {
+ ExtensionHost* toolstrip = model_->ToolstripAt(i);
+ if (view == toolstrip->view())
+ return toolstrip;
+ }
+ NOTREACHED();
+ return NULL;
+}
+
+void ExtensionShelf::ShowShelfHandle() {
+ if (drag_placeholder_view_)
+ return;
+ if (!timer_factory_.empty())
+ timer_factory_.RevokeAll();
+ if (handle_visible_) {
+ // The contents may have changed, even though the handle is still visible.
+ LayoutShelfHandle();
+ return;
+ }
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ timer_factory_.NewRunnableMethod(&ExtensionShelf::DoShowShelfHandle),
+ 1000);
+}
+
+void ExtensionShelf::DoShowShelfHandle() {
+ if (!handle_visible_) {
+ handle_visible_ = true;
+ LayoutShelfHandle();
+ handle_->Show();
+ }
+}
+
+void ExtensionShelf::HideShelfHandle(int delay_ms) {
+ if (drag_placeholder_view_)
+ return;
+ if (!timer_factory_.empty())
+ timer_factory_.RevokeAll();
+ if (!handle_visible_)
+ return;
+ if (delay_ms) {
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ timer_factory_.NewRunnableMethod(&ExtensionShelf::DoHideShelfHandle),
+ delay_ms);
+ } else {
+ DoHideShelfHandle();
+ }
+}
+
+void ExtensionShelf::DoHideShelfHandle() {
+ if (handle_visible_) {
+ handle_visible_ = false;
+ handle_->Hide();
+ handle_->DetachFromBrowser();
+ handle_.reset(NULL);
+ current_toolstrip_ = NULL;
+ }
+}
+
+void ExtensionShelf::LayoutShelfHandle() {
+ if (current_toolstrip_) {
+ GetHandle(); // ensure that the handle exists since we delete on hide
+ ExtensionShelfHandle* handle_view =
+ static_cast<ExtensionShelfHandle*>(GetHandle()->view());
+ handle_view->SetExtensionView(current_toolstrip_->view());
+ int width = std::max(current_toolstrip_->view()->width(),
+ handle_view->width());
+ gfx::Point origin(-kToolstripPadding,
+ -(handle_view->height() + kToolstripPadding - 1));
+ views::View::ConvertPointToWidget(current_toolstrip_->view(), &origin);
+ handle_view->SetBounds(0, 0, width, handle_view->height());
+ handle_->SetBounds(origin.x(), origin.y(),
+ width, handle_view->height());
+ }
+}
+
+void ExtensionShelf::LoadFromModel() {
+ int count = model_->count();
+ for (int i = 0; i < count; ++i)
+ ToolstripInsertedAt(model_->ToolstripAt(i), i);
+}
diff --git a/chrome/browser/views/extensions/extension_shelf.h b/chrome/browser/views/extensions/extension_shelf.h
new file mode 100644
index 0000000..a4baaec2
--- /dev/null
+++ b/chrome/browser/views/extensions/extension_shelf.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_VIEWS_EXTENSIONS_EXTENSION_SHELF_H_
+#define CHROME_BROWSER_VIEWS_EXTENSIONS_EXTENSION_SHELF_H_
+
+#include "app/gfx/canvas.h"
+#include "base/task.h"
+#include "chrome/browser/extensions/extension_shelf_model.h"
+#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/browser/views/browser_bubble.h"
+#include "views/view.h"
+
+class Browser;
+class ExtensionShelfHandle;
+namespace views {
+ class Label;
+ class MouseEvent;
+}
+
+// A shelf that contains Extension toolstrips.
+class ExtensionShelf : public views::View,
+ public ExtensionContainer,
+ public BrowserBubble::Delegate,
+ public ExtensionShelfModelObserver {
+ public:
+ explicit ExtensionShelf(Browser* browser);
+ virtual ~ExtensionShelf();
+
+ // Return the current active ExtensionShelfHandle (if any).
+ BrowserBubble* GetHandle();
+
+ // View
+ virtual void Paint(gfx::Canvas* canvas);
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+ virtual void OnMouseExited(const views::MouseEvent& event);
+ virtual void OnMouseEntered(const views::MouseEvent& event);
+
+ // ExtensionContainer
+ virtual void OnExtensionMouseEvent(ExtensionView* view);
+ virtual void OnExtensionMouseLeave(ExtensionView* view);
+
+ // BrowserBubble::Delegate
+ virtual void BubbleBrowserWindowMoved(BrowserBubble* bubble);
+ virtual void BubbleBrowserWindowClosed(BrowserBubble* bubble);
+
+ // ExtensionShelfModelObserver
+ virtual void ToolstripInsertedAt(ExtensionHost* toolstrip, int index);
+ virtual void ToolstripRemovingAt(ExtensionHost* toolstrip, int index);
+ virtual void ToolstripDraggingFrom(ExtensionHost* toolstrip, int index);
+ virtual void ToolstripMoved(ExtensionHost* toolstrip,
+ int from_index,
+ int to_index);
+ virtual void ToolstripChangedAt(ExtensionHost* toolstrip, int index);
+ virtual void ExtensionShelfEmpty();
+ virtual void ShelfModelReloaded();
+
+ // Dragging toolstrips
+ void DragExtension();
+ void DropExtension(const gfx::Point& pt, bool cancel);
+ void DragHandleTo(const gfx::Point& pt);
+
+ protected:
+ // View
+ virtual void ChildPreferredSizeChanged(View* child);
+
+ private:
+ // Inits the background bitmap.
+ void InitBackground(gfx::Canvas* canvas, const SkRect& subset);
+
+ // Returns the toolstrip at |x| coordinate. If |x| is < 0, returns
+ // the first toolstrip. If |x| > the last toolstrip position, the
+ // last toolstrip is returned.
+ ExtensionHost* ToolstripAtX(int x);
+
+ // Returns the toolstrip associated with |view|.
+ ExtensionHost* ToolstripForView(ExtensionView* view);
+
+ // Show / Hide the shelf handle.
+ void ShowShelfHandle();
+ void DoShowShelfHandle();
+ void HideShelfHandle(int delay_ms);
+ void DoHideShelfHandle();
+
+ // Adjust shelf handle size and position.
+ void LayoutShelfHandle();
+
+ // Loads initial state from |model_|.
+ void LoadFromModel();
+
+ // Background bitmap to draw under extension views.
+ SkBitmap background_;
+
+ // The current shelf handle.
+ scoped_ptr<BrowserBubble> handle_;
+
+ // Whether to handle is visible;
+ bool handle_visible_;
+
+ // Which child view the handle is currently over.
+ ExtensionHost* current_toolstrip_;
+
+ // Timers for tracking mouse hovering.
+ ScopedRunnableMethodFactory<ExtensionShelf> timer_factory_;
+
+ // A placeholder for a pending drag
+ View* drag_placeholder_view_;
+
+ // The model representing the toolstrips on the shelf.
+ scoped_ptr<ExtensionShelfModel> model_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionShelf);
+};
+
+#endif // CHROME_BROWSER_VIEWS_EXTENSIONS_EXTENSION_SHELF_H_
diff --git a/chrome/browser/views/extensions/extension_view.cc b/chrome/browser/views/extensions/extension_view.cc
new file mode 100644
index 0000000..2050aa0
--- /dev/null
+++ b/chrome/browser/views/extensions/extension_view.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/views/extensions/extension_view.h"
+
+#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/renderer_host/render_widget_host_view.h"
+#if defined(OS_WIN)
+#include "chrome/browser/renderer_host/render_widget_host_view_win.h"
+#endif
+#include "views/widget/widget.h"
+
+ExtensionView::ExtensionView(ExtensionHost* host, Browser* browser)
+ : host_(host), browser_(browser),
+ initialized_(false), pending_preferred_width_(0), container_(NULL),
+ did_insert_css_(false), is_clipped_(false) {
+ host_->set_view(this);
+}
+
+ExtensionView::~ExtensionView() {
+ CleanUp();
+}
+
+Extension* ExtensionView::extension() const {
+ return host_->extension();
+}
+
+RenderViewHost* ExtensionView::render_view_host() const {
+ return host_->render_view_host();
+}
+
+void ExtensionView::SetDidInsertCSS(bool did_insert) {
+ did_insert_css_ = did_insert;
+ ShowIfCompletelyLoaded();
+}
+
+void ExtensionView::SetVisible(bool is_visible) {
+ if (is_visible != IsVisible()) {
+ NativeViewHost::SetVisible(is_visible);
+
+ // Also tell RenderWidgetHostView the new visibility. Despite its name, it
+ // is not part of the View heirarchy and does not know about the change
+ // unless we tell it.
+ if (render_view_host()->view()) {
+ if (is_visible)
+ render_view_host()->view()->Show();
+ else
+ render_view_host()->view()->Hide();
+ }
+ }
+}
+
+void ExtensionView::DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current) {
+ View::DidChangeBounds(previous, current);
+ // Propagate the new size to RenderWidgetHostView.
+ // We can't send size zero because RenderWidget DCHECKs that.
+ if (render_view_host()->view() && !current.IsEmpty())
+ render_view_host()->view()->SetSize(gfx::Size(width(), height()));
+}
+
+void ExtensionView::CreateWidgetHostView() {
+ DCHECK(!initialized_);
+ initialized_ = true;
+ RenderWidgetHostView* view =
+ RenderWidgetHostView::CreateViewForWidget(render_view_host());
+
+ // TODO(mpcomplete): RWHV needs a cross-platform Init function.
+#if defined(OS_WIN)
+ // Create the HWND. Note:
+ // RenderWidgetHostHWND supports windowed plugins, but if we ever also
+ // wanted to support constrained windows with this, we would need an
+ // additional HWND to parent off of because windowed plugin HWNDs cannot
+ // exist in the same z-order as constrained windows.
+ RenderWidgetHostViewWin* view_win =
+ static_cast<RenderWidgetHostViewWin*>(view);
+ HWND hwnd = view_win->Create(GetWidget()->GetNativeView());
+ view_win->ShowWindow(SW_SHOW);
+ Attach(hwnd);
+#else
+ NOTIMPLEMENTED();
+#endif
+
+ host_->CreateRenderView(view);
+ SetVisible(false);
+
+ if (!pending_background_.empty()) {
+ render_view_host()->view()->SetBackground(pending_background_);
+ pending_background_.reset();
+ }
+}
+
+void ExtensionView::ShowIfCompletelyLoaded() {
+ // We wait to show the ExtensionView until it has loaded, our parent has
+ // given us a background and css has been inserted into page. These can happen
+ // in different orders.
+ if (!IsVisible() && host_->did_stop_loading() && render_view_host()->view() &&
+ !is_clipped_ && did_insert_css_ &&
+ !render_view_host()->view()->background().empty()) {
+ SetVisible(true);
+ DidContentsPreferredWidthChange(pending_preferred_width_);
+ }
+}
+
+void ExtensionView::CleanUp() {
+ if (!initialized_)
+ return;
+ if (native_view())
+ Detach();
+ initialized_ = false;
+}
+
+void ExtensionView::SetBackground(const SkBitmap& background) {
+ if (initialized_ && render_view_host()->view()) {
+ render_view_host()->view()->SetBackground(background);
+ } else {
+ pending_background_ = background;
+ }
+ ShowIfCompletelyLoaded();
+}
+
+void ExtensionView::DidContentsPreferredWidthChange(const int pref_width) {
+ // Don't actually do anything with this information until we have been shown.
+ // Size changes will not be honored by lower layers while we are hidden.
+ if (!IsVisible()) {
+ pending_preferred_width_ = pref_width;
+ } else if (pref_width > 0 && pref_width != GetPreferredSize().width()) {
+ SetPreferredSize(gfx::Size(pref_width, height()));
+ }
+}
+
+void ExtensionView::ViewHierarchyChanged(bool is_add,
+ views::View *parent,
+ views::View *child) {
+ NativeViewHost::ViewHierarchyChanged(is_add, parent, child);
+ if (is_add && GetWidget() && !initialized_)
+ CreateWidgetHostView();
+}
+
+void ExtensionView::RecoverCrashedExtension() {
+ CleanUp();
+ CreateWidgetHostView();
+}
+
+void ExtensionView::HandleMouseEvent() {
+ if (container_)
+ container_->OnExtensionMouseEvent(this);
+}
+
+void ExtensionView::HandleMouseLeave() {
+ if (container_)
+ container_->OnExtensionMouseLeave(this);
+}
diff --git a/chrome/browser/views/extensions/extension_view.h b/chrome/browser/views/extensions/extension_view.h
new file mode 100644
index 0000000..de34aa5
--- /dev/null
+++ b/chrome/browser/views/extensions/extension_view.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_VIEWS_EXTENSIONS_EXTENSION_VIEW_H_
+#define CHROME_BROWSER_VIEWS_EXTENSIONS_EXTENSION_VIEW_H_
+
+#include "build/build_config.h"
+
+#include "base/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "views/controls/native/native_view_host.h"
+
+class Browser;
+class Extension;
+class ExtensionHost;
+class ExtensionView;
+class RenderViewHost;
+
+// A class that represents the container that this view is in.
+// (bottom shelf, side bar, etc.)
+class ExtensionContainer {
+ public:
+ // Mouse event notifications from the view. (useful for hover UI).
+ virtual void OnExtensionMouseEvent(ExtensionView* view) = 0;
+ virtual void OnExtensionMouseLeave(ExtensionView* view) = 0;
+};
+
+// This handles the display portion of an ExtensionHost.
+class ExtensionView : public views::NativeViewHost {
+ public:
+ ExtensionView(ExtensionHost* host, Browser* browser);
+ ~ExtensionView();
+
+ ExtensionHost* host() const { return host_; }
+ Browser* browser() const { return browser_; }
+ Extension* extension() const;
+ RenderViewHost* render_view_host() const;
+ void SetDidInsertCSS(bool did_insert);
+ void set_is_clipped(bool is_clipped) { is_clipped_ = is_clipped; }
+
+ // Notification from ExtensionHost.
+ void DidContentsPreferredWidthChange(const int pref_width);
+ void HandleMouseEvent();
+ void HandleMouseLeave();
+
+ // Set a custom background for the view. The background will be tiled.
+ void SetBackground(const SkBitmap& background);
+
+ // Sets the container for this view.
+ void SetContainer(ExtensionContainer* container) { container_ = container; }
+
+ // Overridden from views::NativeViewHost:
+ virtual void SetVisible(bool is_visible);
+ virtual void DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current);
+ virtual void ViewHierarchyChanged(bool is_add,
+ views::View *parent, views::View *child);
+
+ // Call after extension process crash to re-initialize view, so that
+ // extension content can be rendered again.
+ void RecoverCrashedExtension();
+
+ private:
+ friend class ExtensionHost;
+
+ // Initializes the RenderWidgetHostView for this object.
+ void CreateWidgetHostView();
+
+ // We wait to show the ExtensionView until several things have loaded.
+ void ShowIfCompletelyLoaded();
+
+ // Restore object to initial state. Called on shutdown or after a renderer
+ // crash.
+ void CleanUp();
+
+ // The running extension instance that we're displaying.
+ // Note that host_ owns view
+ ExtensionHost* host_;
+
+ // The browser window that this view is in.
+ Browser* browser_;
+
+ // True if we've been initialized.
+ bool initialized_;
+
+ // The background the view should have once it is initialized. This is set
+ // when the view has a custom background, but hasn't been initialized yet.
+ SkBitmap pending_background_;
+
+ // What we should set the preferred width to once the ExtensionView has
+ // loaded.
+ int pending_preferred_width_;
+
+ // The container this view is in (not necessarily its direct superview).
+ // Note: the view does not own its container.
+ ExtensionContainer* container_;
+
+ // Whether the RenderView has inserted extension css into toolstrip page.
+ bool did_insert_css_;
+
+ // Whether this extension view is clipped.
+ bool is_clipped_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_EXTENSIONS_EXTENSION_VIEW_H_