diff options
Diffstat (limited to 'chrome/browser/views')
-rw-r--r-- | chrome/browser/views/bookmark_bar_view.cc | 3 | ||||
-rw-r--r-- | chrome/browser/views/extensions/extension_shelf.cc | 589 | ||||
-rw-r--r-- | chrome/browser/views/extensions/extension_shelf.h | 117 | ||||
-rw-r--r-- | chrome/browser/views/extensions/extension_view.cc | 155 | ||||
-rw-r--r-- | chrome/browser/views/extensions/extension_view.h | 109 |
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_ |