diff options
author | erikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-10 00:11:24 +0000 |
---|---|---|
committer | erikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-10 00:11:24 +0000 |
commit | ab4eaf7860c00f15d72321213ec694d6ad20c47e (patch) | |
tree | 87daa29807baa4bb74b4038ec13cabe0e3ffb240 /chrome/browser/extensions | |
parent | 2f25d7b91d75afea74cf4ba9e3b2a2db0d853f50 (diff) | |
download | chromium_src-ab4eaf7860c00f15d72321213ec694d6ad20c47e.zip chromium_src-ab4eaf7860c00f15d72321213ec694d6ad20c47e.tar.gz chromium_src-ab4eaf7860c00f15d72321213ec694d6ad20c47e.tar.bz2 |
part 2 of dragging change: reordering, but not persisting
* split up extension_shelf into a model and a view (easier to port)
* ExtensionHost now owns ExtensionView rather than vice versa
* dragging reorders extensions on the shelf
* moved ExtensionView* into browser_tests (currently not working)
BUG=12123
TEST=browser_tests.exe --gtest_filter=ExtensionShelfModel* (browser_tests.exe is currently broken)
Review URL: http://codereview.chromium.org/119290
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18002 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r-- | chrome/browser/extensions/extension_host.cc | 34 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_host.h | 21 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_process_manager.cc | 24 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_process_manager.h | 11 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_shelf.cc | 240 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_shelf.h | 47 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_shelf_model.cc | 133 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_shelf_model.h | 109 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_shelf_model_unittest.cc | 114 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_view.cc | 15 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_view.h | 18 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_view_unittest.cc | 14 |
12 files changed, 580 insertions, 200 deletions
diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc index c9eeda8..8ccc9c7 100644 --- a/chrome/browser/extensions/extension_host.cc +++ b/chrome/browser/extensions/extension_host.cc @@ -11,7 +11,6 @@ #include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extension_process_manager.h" -#include "chrome/browser/extensions/extension_view.h" #include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_process_host.h" @@ -30,13 +29,11 @@ #include "webkit/glue/context_menu.h" ExtensionHost::ExtensionHost(Extension* extension, SiteInstance* site_instance, - ExtensionProcessManager* manager) + const GURL& url, ExtensionProcessManager* manager) : extension_(extension), manager_(manager), -#if defined(OS_WIN) - view_(NULL), -#endif - did_stop_loading_(false) { + did_stop_loading_(false), + url_(url) { render_view_host_ = new RenderViewHost( site_instance, this, MSG_ROUTING_NONE, NULL); render_view_host_->AllowExtensionBindings(); @@ -48,6 +45,15 @@ ExtensionHost::~ExtensionHost() { render_view_host_->Shutdown(); // deletes render_view_host } +void ExtensionHost::CreateView(Browser* browser) { +#if defined(TOOLKIT_VIEWS) + view_.reset(new ExtensionView(this, browser)); +#else + // TODO(port) + NOTREACHED(); +#endif +} + RenderProcessHost* ExtensionHost::render_process_host() const { return render_view_host_->process(); } @@ -56,17 +62,15 @@ SiteInstance* ExtensionHost::site_instance() const { return render_view_host_->site_instance(); } -void ExtensionHost::CreateRenderView(const GURL& url, - RenderWidgetHostView* host_view) { - url_ = url; +void ExtensionHost::CreateRenderView(RenderWidgetHostView* host_view) { render_view_host_->set_view(host_view); render_view_host_->CreateRenderView(); - render_view_host_->NavigateToURL(url); + render_view_host_->NavigateToURL(url_); } void ExtensionHost::UpdatePreferredWidth(int pref_width) { #if defined(OS_WIN) - if (view_) + if (view_.get()) view_->DidContentsPreferredWidthChange(pref_width); #endif } @@ -102,7 +106,7 @@ void ExtensionHost::DidStopLoading(RenderViewHost* render_view_host) { did_stop_loading_ = true; #if defined(OS_WIN) - if (view_) + if (view_.get()) view_->ShowIfCompletelyLoaded(); #endif } @@ -184,21 +188,21 @@ void ExtensionHost::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { void ExtensionHost::HandleMouseEvent() { #if defined(OS_WIN) - if (view_) + if (view_.get()) view_->HandleMouseEvent(); #endif } void ExtensionHost::HandleMouseLeave() { #if defined(OS_WIN) - if (view_) + if (view_.get()) view_->HandleMouseLeave(); #endif } Browser* ExtensionHost::GetBrowser() { #if defined(OS_WIN) - if (view_) + if (view_.get()) return view_->browser(); #endif Browser* browser = BrowserList::GetLastActiveWithProfile( diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h index 751e07f..04ee8f3 100644 --- a/chrome/browser/extensions/extension_host.h +++ b/chrome/browser/extensions/extension_host.h @@ -7,13 +7,16 @@ #include <string> +#include "base/scoped_ptr.h" #include "chrome/browser/renderer_host/render_view_host_delegate.h" #include "chrome/browser/tab_contents/render_view_host_delegate_helper.h" +#if defined(TOOLKIT_VIEWS) +#include "chrome/browser/extensions/extension_view.h" +#endif class Browser; class Extension; class ExtensionProcessManager; -class ExtensionView; class RenderProcessHost; class RenderWidgetHost; class RenderWidgetHostView; @@ -29,13 +32,17 @@ class ExtensionHost : public RenderViewHostDelegate, public ExtensionFunctionDispatcher::Delegate { public: ExtensionHost(Extension* extension, SiteInstance* site_instance, - ExtensionProcessManager* manager); + const GURL& url, ExtensionProcessManager* manager); ~ExtensionHost(); #if defined(TOOLKIT_VIEWS) - void set_view(ExtensionView* view) { view_ = view; } - ExtensionView* view() const { return view_; } + void set_view(ExtensionView* view) { view_.reset(view); } + ExtensionView* view() const { return view_.get(); } #endif + + // Create an ExtensionView and tie it to this host and |browser|. + void CreateView(Browser* browser); + Extension* extension() { return extension_; } RenderViewHost* render_view_host() const { return render_view_host_; } RenderProcessHost* render_process_host() const; @@ -43,9 +50,9 @@ class ExtensionHost : public RenderViewHostDelegate, bool did_stop_loading() const { return did_stop_loading_; } // Initializes our RenderViewHost by creating its RenderView and navigating - // to the given URL. Uses host_view for the RenderViewHost's view (can be + // to this host's url. Uses host_view for the RenderViewHost's view (can be // NULL). - void CreateRenderView(const GURL& url, RenderWidgetHostView* host_view); + void CreateRenderView(RenderWidgetHostView* host_view); // RenderViewHostDelegate virtual const GURL& GetURL() const { return url_; } @@ -97,7 +104,7 @@ class ExtensionHost : public RenderViewHostDelegate, #if defined(TOOLKIT_VIEWS) // Optional view that shows the rendered content in the UI. - ExtensionView* view_; + scoped_ptr<ExtensionView> view_; #endif // The host for our HTML content. diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc index 3aab7fb..5f1eada 100644 --- a/chrome/browser/extensions/extension_process_manager.cc +++ b/chrome/browser/extensions/extension_process_manager.cc @@ -6,9 +6,6 @@ #include "chrome/browser/browsing_instance.h" #include "chrome/browser/extensions/extension_host.h" -#if defined(TOOLKIT_VIEWS) -#include "chrome/browser/extensions/extension_view.h" -#endif #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/site_instance.h" @@ -45,24 +42,23 @@ ExtensionProcessManager::~ExtensionProcessManager() { delete *iter; } -#if defined(TOOLKIT_VIEWS) -ExtensionView* ExtensionProcessManager::CreateView(Extension* extension, +ExtensionHost* ExtensionProcessManager::CreateView(Extension* extension, const GURL& url, Browser* browser) { - ExtensionHost* host = new ExtensionHost(extension, - GetSiteInstanceForURL(url), this); - all_hosts_.insert(host); - return new ExtensionView(host, browser, url); + ExtensionHost* host = + new ExtensionHost(extension, GetSiteInstanceForURL(url), url, this); + host->CreateView(browser); + return host; } -#endif -void ExtensionProcessManager::CreateBackgroundHost(Extension* extension, - const GURL& url) { +ExtensionHost* ExtensionProcessManager::CreateBackgroundHost( + Extension* extension, const GURL& url) { ExtensionHost* host = - new ExtensionHost(extension, GetSiteInstanceForURL(url), this); - host->CreateRenderView(url, NULL); // create a RenderViewHost with no view + new ExtensionHost(extension, GetSiteInstanceForURL(url), url, this); + host->CreateRenderView(NULL); // create a RenderViewHost with no view all_hosts_.insert(host); background_hosts_.insert(host); + return host; } SiteInstance* ExtensionProcessManager::GetSiteInstanceForURL(const GURL& url) { diff --git a/chrome/browser/extensions/extension_process_manager.h b/chrome/browser/extensions/extension_process_manager.h index 48ce382..23fcb25 100644 --- a/chrome/browser/extensions/extension_process_manager.h +++ b/chrome/browser/extensions/extension_process_manager.h @@ -28,17 +28,16 @@ class ExtensionProcessManager : public NotificationObserver { explicit ExtensionProcessManager(Profile* profile); ~ExtensionProcessManager(); -#if defined(TOOLKIT_VIEWS) - // Creates a new ExtensionView, grouping it in the appropriate SiteInstance - // (and therefore process) based on the URL and profile. - ExtensionView* CreateView(Extension* extension, + // Creates a new ExtensionHost with its associated view, grouping it in the + // appropriate SiteInstance (and therefore process) based on the URL and + // profile. + ExtensionHost* CreateView(Extension* extension, const GURL& url, Browser* browser); -#endif // Creates a new UI-less extension instance. Like CreateView, but not // displayed anywhere. - void CreateBackgroundHost(Extension* extension, const GURL& url); + ExtensionHost* CreateBackgroundHost(Extension* extension, const GURL& url); // Returns the SiteInstance that the given URL belongs to. SiteInstance* GetSiteInstanceForURL(const GURL& url); diff --git a/chrome/browser/extensions/extension_shelf.cc b/chrome/browser/extensions/extension_shelf.cc index 782ddff..3978d19 100644 --- a/chrome/browser/extensions/extension_shelf.cc +++ b/chrome/browser/extensions/extension_shelf.cc @@ -179,7 +179,9 @@ bool ExtensionShelfHandle::OnMousePressed(const views::MouseEvent& event) { bool ExtensionShelfHandle::OnMouseDragged(const views::MouseEvent& event) { if (!dragging_) { int y_delta = abs(initial_drag_location_.y() - event.location().y()); - if (y_delta > GetVerticalDragThreshold()) { + int x_delta = abs(initial_drag_location_.x() - event.location().x()); + if (y_delta > GetVerticalDragThreshold() || + x_delta > GetHorizontalDragThreshold()) { dragging_ = true; shelf_->DragExtension(); } @@ -204,40 +206,36 @@ void ExtensionShelfHandle::OnMouseReleased(const views::MouseEvent& event, if (dragging_) { views::View::OnMouseReleased(event, canceled); dragging_ = false; - shelf_->DropExtension(event.location(), canceled); + // |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) - : browser_(browser), - handle_(NULL), + : handle_(NULL), handle_visible_(false), - current_handle_view_(NULL), + current_toolstrip_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(timer_factory_(this)), - drag_placeholder_view_(NULL) { - // Watch extensions loaded and unloaded notifications. - registrar_.Add(this, NotificationType::EXTENSIONS_LOADED, - NotificationService::AllSources()); - registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, - NotificationService::AllSources()); - - // Add any already-loaded extensions now, since we missed the notification for - // those. - ExtensionsService* service = browser_->profile()->GetExtensionsService(); - if (service) { // This can be null in unit tests. - if (AddExtensionViews(service->extensions())) { - Layout(); - SchedulePaint(); - } - } + drag_placeholder_view_(NULL), + model_(new ExtensionShelfModel(browser)) { + model_->AddObserver(this); + LoadFromModel(); +} + +ExtensionShelf::~ExtensionShelf() { + model_->RemoveObserver(this); } BrowserBubble* ExtensionShelf::GetHandle() { - if (!handle_.get() && HasExtensionViews() && current_handle_view_) { + if (!handle_.get() && current_toolstrip_) { ExtensionShelfHandle* handle_view = new ExtensionShelfHandle(this); - handle_view->SetExtensionView(current_handle_view_); + handle_view->SetExtensionView(current_toolstrip_->view()); handle_.reset(new BrowserBubble(handle_view, GetWidget(), gfx::Point(0, 0))); handle_->set_delegate(this); @@ -278,7 +276,7 @@ void ExtensionShelf::Paint(gfx::Canvas* canvas) { } gfx::Size ExtensionShelf::GetPreferredSize() { - if (HasExtensionViews()) + if (model_->count()) return gfx::Size(0, kShelfHeight); return gfx::Size(0, 0); } @@ -296,10 +294,11 @@ void ExtensionShelf::Layout() { int content_height = height() - kTopMargin - kBottomMargin; int max_x = width() - kRightMargin; - int count = GetChildViewCount(); + int count = model_->count(); for (int i = 0; i < count; ++i) { x += kToolstripPadding; // left padding - views::View* child = GetChildViewAt(i); + ExtensionHost* toolstrip = model_->ToolstripAt(i); + views::View* child = toolstrip->view(); gfx::Size pref = child->GetPreferredSize(); int next_x = x + pref.width() + kToolstripPadding; // right padding child->SetVisible(next_x < max_x); @@ -313,14 +312,10 @@ void ExtensionShelf::Layout() { } void ExtensionShelf::OnMouseEntered(const views::MouseEvent& event) { - int count = GetChildViewCount(); - for (int i = 0; i < count; ++i) { - ExtensionView* child = static_cast<ExtensionView*>(GetChildViewAt(i)); - if (event.x() > (child->x() + child->width() + kToolstripPadding)) - continue; - current_handle_view_ = child; + ExtensionHost* toolstrip = ToolstripAtX(event.x()); + if (toolstrip) { + current_toolstrip_ = toolstrip; ShowShelfHandle(); - break; } } @@ -328,98 +323,62 @@ void ExtensionShelf::OnMouseExited(const views::MouseEvent& event) { HideShelfHandle(kHideDelayMs); } -void ExtensionShelf::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - switch (type.value) { - case NotificationType::EXTENSIONS_LOADED: { - const ExtensionList* extensions = Details<ExtensionList>(details).ptr(); - AddExtensionViews(extensions); - break; - } - case NotificationType::EXTENSION_UNLOADED: { - Extension* extension = Details<Extension>(details).ptr(); - RemoveExtensionViews(extension); - break; - } - default: - DCHECK(false) << "Unhandled notification of type: " << type.value; - break; - } +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(); } -bool ExtensionShelf::AddExtensionViews(const ExtensionList* extensions) { - bool had_views = HasExtensionViews(); - bool added_toolstrip = false; - ExtensionProcessManager* manager = - browser_->profile()->GetExtensionProcessManager(); - for (ExtensionList::const_iterator extension = extensions->begin(); - extension != extensions->end(); ++extension) { - for (std::vector<std::string>::const_iterator toolstrip_path = - (*extension)->toolstrips().begin(); - toolstrip_path != (*extension)->toolstrips().end(); ++toolstrip_path) { - ExtensionView* toolstrip = - manager->CreateView(*extension, - (*extension)->GetResourceURL(*toolstrip_path), - browser_); - if (!background_.empty()) - toolstrip->SetBackground(background_); - AddChildView(toolstrip); - toolstrip->SetContainer(this); - added_toolstrip = true; - } - } - if (added_toolstrip) { - SchedulePaint(); - if (!had_views) - PreferredSizeChanged(); - } - return added_toolstrip; +void ExtensionShelf::ToolstripRemovingAt(ExtensionHost* toolstrip, + int index) { + ExtensionView* view = toolstrip->view(); + RemoveChildView(view); + Layout(); } -bool ExtensionShelf::RemoveExtensionViews(Extension* extension) { - if (!HasExtensionViews()) - return false; +void ExtensionShelf::ToolstripDraggingFrom(ExtensionHost* toolstrip, + int index) { +} - bool removed_toolstrip = false; - int count = GetChildViewCount(); - for (int i = count - 1; i >= 0; --i) { - ExtensionView* view = static_cast<ExtensionView*>(GetChildViewAt(i)); - if (view->host()->extension()->id() == extension->id()) { - RemoveChildView(view); - delete view; - removed_toolstrip = true; - } - } +void ExtensionShelf::ToolstripMoved(ExtensionHost* toolstrip, + int from_index, + int to_index) { + Layout(); +} - if (removed_toolstrip) { - SchedulePaint(); - PreferredSizeChanged(); - } - return removed_toolstrip; +void ExtensionShelf::ToolstripChangedAt(ExtensionHost* toolstrip, + int index) { } -bool ExtensionShelf::HasExtensionViews() { - return GetChildViewCount() > 0; +void ExtensionShelf::ExtensionShelfEmpty() { + PreferredSizeChanged(); } void ExtensionShelf::OnExtensionMouseEvent(ExtensionView* view) { // Ignore these events when dragging. if (drag_placeholder_view_) return; - if (view != current_handle_view_) { - current_handle_view_ = view; - } - ShowShelfHandle(); + 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; - if (view == current_handle_view_) { + ExtensionHost *toolstrip = ToolstripForView(view); + if (toolstrip == current_toolstrip_) HideShelfHandle(kHideDelayMs); - } } void ExtensionShelf::BubbleBrowserWindowMoved(BrowserBubble* bubble) { @@ -435,15 +394,14 @@ void ExtensionShelf::DragExtension() { // Construct a placeholder view to replace the view. // TODO(erikkay) the placeholder should draw a dimmed version of the // extension view - int index = GetChildIndex(current_handle_view_); drag_placeholder_view_ = new View(); - drag_placeholder_view_->SetBounds(current_handle_view_->bounds()); - AddChildView(index, drag_placeholder_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_handle_view_); + handle_view->AddChildView(current_toolstrip_->view()); handle_view->SizeToPreferredSize(); handle_->ResizeToView(); handle_view->Layout(); @@ -455,13 +413,19 @@ void ExtensionShelf::DropExtension(const gfx::Point& pt, bool cancel) { handle_->AttachToBrowser(); // Replace the placeholder view with the original. - int index = GetChildIndex(drag_placeholder_view_); - AddChildView(index, current_handle_view_); - current_handle_view_->SetBounds(drag_placeholder_view_->bounds()); + 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(); @@ -473,6 +437,8 @@ void ExtensionShelf::DropExtension(const gfx::Point& pt, bool cancel) { 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, @@ -505,9 +471,39 @@ void ExtensionShelf::InitBackground(gfx::Canvas* canvas, DCHECK(background_.readyToDraw()); // Tell all extension views about the new background - int count = GetChildViewCount(); + int count = model_->count(); for (int i = 0; i < count; ++i) - static_cast<ExtensionView*>(GetChildViewAt(i))->SetBackground(background_); + 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() { @@ -555,22 +551,30 @@ void ExtensionShelf::DoHideShelfHandle() { handle_->Hide(); handle_->DetachFromBrowser(); handle_.reset(NULL); - current_handle_view_ = NULL; + current_toolstrip_ = NULL; } } void ExtensionShelf::LayoutShelfHandle() { - if (current_handle_view_) { + 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_handle_view_); - int width = std::max(current_handle_view_->width(), handle_view->width()); + 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_handle_view_, &origin); + 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/extensions/extension_shelf.h b/chrome/browser/extensions/extension_shelf.h index 7ca5cd6..7cc1a638 100644 --- a/chrome/browser/extensions/extension_shelf.h +++ b/chrome/browser/extensions/extension_shelf.h @@ -7,11 +7,10 @@ #include "app/gfx/canvas.h" #include "base/task.h" +#include "chrome/browser/extensions/extension_shelf_model.h" #include "chrome/browser/extensions/extension_view.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/views/browser_bubble.h" -#include "chrome/common/notification_observer.h" -#include "chrome/common/notification_registrar.h" #include "views/view.h" class Browser; @@ -23,11 +22,12 @@ namespace views { // A shelf that contains Extension toolstrips. class ExtensionShelf : public views::View, - public NotificationObserver, public ExtensionContainer, - public BrowserBubble::Delegate { + public BrowserBubble::Delegate, + public ExtensionShelfModelObserver { public: explicit ExtensionShelf(Browser* browser); + virtual ~ExtensionShelf(); // Return the current active ExtensionShelfHandle (if any). BrowserBubble* GetHandle(); @@ -39,14 +39,6 @@ class ExtensionShelf : public views::View, virtual void OnMouseExited(const views::MouseEvent& event); virtual void OnMouseEntered(const views::MouseEvent& event); - // NotificationService - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - bool AddExtensionViews(const ExtensionList* extensions); - bool HasExtensionViews(); - // ExtensionContainer virtual void OnExtensionMouseEvent(ExtensionView* view); virtual void OnExtensionMouseLeave(ExtensionView* view); @@ -55,6 +47,16 @@ class ExtensionShelf : public views::View, 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(); + // Dragging toolstrips void DragExtension(); void DropExtension(const gfx::Point& pt, bool cancel); @@ -68,8 +70,13 @@ class ExtensionShelf : public views::View, // Inits the background bitmap. void InitBackground(gfx::Canvas* canvas, const SkRect& subset); - // Removes any toolstrips associated with an extension. - bool RemoveExtensionViews(Extension* extension); + // 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(); @@ -80,11 +87,8 @@ class ExtensionShelf : public views::View, // Adjust shelf handle size and position. void LayoutShelfHandle(); - // Which browser window this shelf is in. - Browser* browser_; - - // Manages our notification registrations. - NotificationRegistrar registrar_; + // Loads initial state from |model_|. + void LoadFromModel(); // Background bitmap to draw under extension views. SkBitmap background_; @@ -96,7 +100,7 @@ class ExtensionShelf : public views::View, bool handle_visible_; // Which child view the handle is currently over. - ExtensionView* current_handle_view_; + ExtensionHost* current_toolstrip_; // Timers for tracking mouse hovering. ScopedRunnableMethodFactory<ExtensionShelf> timer_factory_; @@ -104,6 +108,9 @@ class ExtensionShelf : public views::View, // 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); }; diff --git a/chrome/browser/extensions/extension_shelf_model.cc b/chrome/browser/extensions/extension_shelf_model.cc new file mode 100644 index 0000000..37d3eb6 --- /dev/null +++ b/chrome/browser/extensions/extension_shelf_model.cc @@ -0,0 +1,133 @@ +// 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/extensions/extension_shelf_model.h" + +#include "chrome/browser/browser.h" +#include "chrome/browser/profile.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/common/extensions/extension.h" +#include "chrome/common/notification_service.h" + +///////////////////////////// + +ExtensionShelfModel::ExtensionShelfModel(Browser* browser) : browser_(browser) { + // Watch extensions loaded and unloaded notifications. + registrar_.Add(this, NotificationType::EXTENSIONS_LOADED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, + NotificationService::AllSources()); + + // Add any already-loaded extensions now, since we missed the notification for + // those. + ExtensionsService* service = browser_->profile()->GetExtensionsService(); + if (service) // This can be null in unit tests. + AddExtensions(service->extensions()); +} + +ExtensionShelfModel::~ExtensionShelfModel() { + DCHECK(observers_.size() == 0); +} + +void ExtensionShelfModel::AddObserver(ExtensionShelfModelObserver* observer) { + observers_.AddObserver(observer); +} + +void ExtensionShelfModel::RemoveObserver( + ExtensionShelfModelObserver* observer) { + observers_.RemoveObserver(observer); +} + +void ExtensionShelfModel::AppendToolstrip(ExtensionHost* toolstrip) { + InsertToolstripAt(count(), toolstrip); +} + +void ExtensionShelfModel::InsertToolstripAt(int index, + ExtensionHost* toolstrip) { + toolstrips_.insert(toolstrips_.begin() + index, toolstrip); + FOR_EACH_OBSERVER(ExtensionShelfModelObserver, observers_, + ToolstripInsertedAt(toolstrip, index)); +} + +void ExtensionShelfModel::RemoveToolstripAt(int index) { + ExtensionHost* toolstrip = ToolstripAt(index); + FOR_EACH_OBSERVER(ExtensionShelfModelObserver, observers_, + ToolstripRemovingAt(toolstrip, index)); + toolstrips_.erase(toolstrips_.begin() + index); + delete toolstrip; +} + +void ExtensionShelfModel::MoveToolstripAt(int index, int to_index) { + if (index == to_index) + return; + + ExtensionHost* toolstrip = toolstrips_.at(index); + toolstrips_.erase(toolstrips_.begin() + index); + toolstrips_.insert(toolstrips_.begin() + to_index, toolstrip); + + FOR_EACH_OBSERVER(ExtensionShelfModelObserver, observers_, + ToolstripMoved(toolstrip, index, to_index)); +} + +int ExtensionShelfModel::IndexOfToolstrip(ExtensionHost* toolstrip) { + ExtensionToolstripList::iterator i = + std::find(toolstrips_.begin(), toolstrips_.end(), toolstrip); + if (i == toolstrips_.end()) + return -1; + return i - toolstrips_.begin(); +} + +ExtensionHost* ExtensionShelfModel::ToolstripAt(int index) { + return toolstrips_[index]; +} + +void ExtensionShelfModel::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::EXTENSIONS_LOADED: { + const ExtensionList* extensions = Details<ExtensionList>(details).ptr(); + AddExtensions(extensions); + break; + } + case NotificationType::EXTENSION_UNLOADED: { + Extension* extension = Details<Extension>(details).ptr(); + RemoveExtension(extension); + break; + } + default: + DCHECK(false) << "Unhandled notification of type: " << type.value; + break; + } +} + +void ExtensionShelfModel::AddExtensions(const ExtensionList* extensions) { + ExtensionProcessManager* manager = + browser_->profile()->GetExtensionProcessManager(); + for (ExtensionList::const_iterator extension = extensions->begin(); + extension != extensions->end(); ++extension) { + for (std::vector<std::string>::const_iterator toolstrip_path = + (*extension)->toolstrips().begin(); + toolstrip_path != (*extension)->toolstrips().end(); ++toolstrip_path) { + ExtensionHost* host = + manager->CreateView(*extension, + (*extension)->GetResourceURL(*toolstrip_path), + browser_); + AppendToolstrip(host); + } + } +} + +void ExtensionShelfModel::RemoveExtension(const Extension* extension) { + for (int i = count() - 1; i >= 0; --i) { + ExtensionHost* t = ToolstripAt(i); + if (t->extension()->id() == extension->id()) { + RemoveToolstripAt(i); + // There can be more than one toolstrip per extension, so we have to keep + // looping even after finding a match. + } + } +} diff --git a/chrome/browser/extensions/extension_shelf_model.h b/chrome/browser/extensions/extension_shelf_model.h new file mode 100644 index 0000000..5b03795 --- /dev/null +++ b/chrome/browser/extensions/extension_shelf_model.h @@ -0,0 +1,109 @@ +// 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_EXTENSIONS_EXTENSION_SHELF_MODEL_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_SHELF_MODEL_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/observer_list.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extensions_service.h" + +class Browser; +class ExtensionView; + +// Objects implement this interface when they wish to be notified of changes to +// the ExtensionShelfModel. +// +// Register your ExtensionShelfModelObserver with the ExtensionShelfModel using +// Add/RemoveObserver methods. +class ExtensionShelfModelObserver { + public: + // A new toolstrip was inserted into ExtensionShelfModel at |index|. + virtual void ToolstripInsertedAt(ExtensionHost* toolstrip, int index) {} + + // The specified toolstrip is being removed and destroyed. + virtual void ToolstripRemovingAt(ExtensionHost* toolstrip, int index) {} + + // |toolstrip| moved from |from_index| to |to_index|. + virtual void ToolstripMoved(ExtensionHost* toolstrip, + int from_index, + int to_index) {} + + // The specified toolstrip changed in some way (currently only size changes) + virtual void ToolstripChangedAt(ExtensionHost* toolstrip, int index) {} + + // There are no more toolstrips in the model. + virtual void ExtensionShelfEmpty() {} + + // TODO(erikkay) - any more? +}; + +// The model representing the toolstrips on an ExtensionShelf. +class ExtensionShelfModel : public NotificationObserver { + public: + ExtensionShelfModel(Browser* browser); + virtual ~ExtensionShelfModel(); + + // Add and remove observers to changes within this ExtensionShelfModel. + void AddObserver(ExtensionShelfModelObserver* observer); + void RemoveObserver(ExtensionShelfModelObserver* observer); + + // The number of toolstrips in the model. + int count() const { return static_cast<int>(toolstrips_.size()); } + bool empty() const { return toolstrips_.empty(); } + + // Add |toolstrip| to the end of the shelf. + void AppendToolstrip(ExtensionHost* toolstrip); + + // Insert |toolstrip| at |index|. + void InsertToolstripAt(int index, ExtensionHost* toolstrip); + + // Remove the toolstrip at |index|. + void RemoveToolstripAt(int index); + + // Move the toolstrip at |index| to |to_index|. + void MoveToolstripAt(int index, int to_index); + + // Lookup the index of |toolstrip|. Returns -1 if not present. + int IndexOfToolstrip(ExtensionHost* toolstrip); + + // Return the toolstrip at |index|. Returns NULL if index is out of range. + ExtensionHost* ToolstripAt(int index); + + // NotificationObserver + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + // Add all of the toolstrips from each extension in |extensions|. + void AddExtensions(const ExtensionList* extensions); + + // Remove all of the toolstrips in |extension| from the shelf. + void RemoveExtension(const Extension* extension); + + // The browser that this model is attached to. + Browser* browser_; + + // Manages our notification registrations. + NotificationRegistrar registrar_; + + // The Toolstrips in this model. + typedef std::vector<ExtensionHost*> ExtensionToolstripList; + ExtensionToolstripList toolstrips_; + + // Our observers. + typedef ObserverList<ExtensionShelfModelObserver> + ExtensionShelfModelObservers; + ExtensionShelfModelObservers observers_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionShelfModel); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SHELF_MODEL_H_ diff --git a/chrome/browser/extensions/extension_shelf_model_unittest.cc b/chrome/browser/extensions/extension_shelf_model_unittest.cc new file mode 100644 index 0000000..230e2cc --- /dev/null +++ b/chrome/browser/extensions/extension_shelf_model_unittest.cc @@ -0,0 +1,114 @@ +// 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/browser.h" +#include "chrome/browser/extensions/extension_shelf_model.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/extensions/test_extension_loader.h" +#include "chrome/browser/profile.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension_error_reporter.h" +#include "chrome/test/in_process_browser_test.h" + +namespace { + +// The extension we're using as our test case. +const char* kExtensionId = "00123456789abcdef0123456789abcdef0123456"; + +}; // namespace + + +// An InProcessBrowserTest for testing the ExtensionShelfModel. +// TODO(erikkay) It's unfortunate that this needs to be an in-proc browser test. +// It would be nice to refactor things so that ExtensionShelfModel, +// ExtensionHost and ExtensionsService could run without so much of the browser +// in place. +class ExtensionShelfModelTest : public InProcessBrowserTest, + public ExtensionShelfModelObserver { + public: + virtual void SetUp() { + // Initialize the error reporter here, or BrowserMain will create it with + // the wrong MessageLoop. + ExtensionErrorReporter::Init(false); + inserted_count_ = 0; + removed_count_ = 0; + moved_count_ = 0; + + InProcessBrowserTest::SetUp(); + } + + virtual void TearDown() { + // Tear down |model_| manually here rather than in the destructor or with + // a scoped_ptr. Since it uses NotificationRegistrar, it needs to clean up + // before the rest of InProcessBrowserTest. + model_->RemoveObserver(this); + delete model_; + model_ = NULL; + InProcessBrowserTest::TearDown(); + } + + virtual void SetUpCommandLine(CommandLine* command_line) { + command_line->AppendSwitch(switches::kEnableExtensions); + } + + virtual Browser* CreateBrowser(Profile* profile) { + Browser* b = InProcessBrowserTest::CreateBrowser(profile); + model_ = new ExtensionShelfModel(b); + model_->AddObserver(this); + return b; + } + + virtual void ToolstripInsertedAt(ExtensionHost* toolstrip, int index) { + inserted_count_++; + } + + virtual void ToolstripRemovingAt(ExtensionHost* toolstrip, int index) { + removed_count_++; + } + + virtual void ToolstripMoved(ExtensionHost* toolstrip, + int from_index, + int to_index) { + moved_count_++; + } + + protected: + ExtensionShelfModel* model_; + + int inserted_count_; + int removed_count_; + int moved_count_; +}; + +IN_PROC_BROWSER_TEST_F(ExtensionShelfModelTest, Basic) { + // Get the path to our extension. + FilePath path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path)); + path = path.AppendASCII("extensions"). + AppendASCII("good").AppendASCII("extension1").AppendASCII("1"); + ASSERT_TRUE(file_util::DirectoryExists(path)); // sanity check + + // Wait for the extension to load and grab a pointer to it. + TestExtensionLoader loader(browser()->profile()); + Extension* extension = loader.Load(kExtensionId, path); + ASSERT_TRUE(extension); + + // extension1 has two toolstrips + EXPECT_EQ(inserted_count_, 2); + ExtensionHost* one = model_->ToolstripAt(0); + ExtensionHost* two = model_->ToolstripAt(1); + EXPECT_EQ(one->GetURL().path(), "/toolstrip1.html"); + EXPECT_EQ(two->GetURL().path(), "/toolstrip2.html"); + + model_->MoveToolstripAt(0, 1); + EXPECT_EQ(two, model_->ToolstripAt(0)); + EXPECT_EQ(one, model_->ToolstripAt(1)); + EXPECT_EQ(moved_count_, 1); + + model_->RemoveToolstripAt(0); + EXPECT_EQ(one, model_->ToolstripAt(0)); + EXPECT_EQ(1, model_->count()); + EXPECT_EQ(removed_count_, 1); +} diff --git a/chrome/browser/extensions/extension_view.cc b/chrome/browser/extensions/extension_view.cc index 40b6d76..7fbd4da 100644 --- a/chrome/browser/extensions/extension_view.cc +++ b/chrome/browser/extensions/extension_view.cc @@ -12,9 +12,8 @@ #endif #include "views/widget/widget.h" -ExtensionView::ExtensionView(ExtensionHost* host, Browser* browser, - const GURL& content_url) - : host_(host), browser_(browser), content_url_(content_url), +ExtensionView::ExtensionView(ExtensionHost* host, Browser* browser) + : host_(host), browser_(browser), initialized_(false), container_(NULL) { host_->set_view(this); } @@ -24,6 +23,14 @@ ExtensionView::~ExtensionView() { Detach(); } +Extension* ExtensionView::extension() const { + return host_->extension(); +} + +RenderViewHost* ExtensionView::render_view_host() const { + return host_->render_view_host(); +} + void ExtensionView::SetVisible(bool is_visible) { if (is_visible != IsVisible()) { NativeViewHost::SetVisible(is_visible); @@ -103,7 +110,7 @@ void ExtensionView::ViewHierarchyChanged(bool is_add, NOTIMPLEMENTED(); #endif - host_->CreateRenderView(content_url_, view); + host_->CreateRenderView(view); SetVisible(false); if (!pending_background_.empty()) { diff --git a/chrome/browser/extensions/extension_view.h b/chrome/browser/extensions/extension_view.h index 06e200d..cf2fc40 100644 --- a/chrome/browser/extensions/extension_view.h +++ b/chrome/browser/extensions/extension_view.h @@ -8,13 +8,15 @@ #include "build/build_config.h" #include "base/scoped_ptr.h" -#include "chrome/browser/extensions/extension_host.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.) @@ -28,13 +30,13 @@ class ExtensionContainer { // This handles the display portion of an ExtensionHost. class ExtensionView : public views::NativeViewHost { public: - ExtensionView(ExtensionHost* host, Browser* browser, const GURL& content_url); + ExtensionView(ExtensionHost* host, Browser* browser); ~ExtensionView(); - ExtensionHost* host() const { return host_.get(); } + ExtensionHost* host() const { return host_; } Browser* browser() const { return browser_; } - Extension* extension() { return host_->extension(); } - RenderViewHost* render_view_host() { return host_->render_view_host(); } + Extension* extension() const; + RenderViewHost* render_view_host() const; // Notification from ExtensionHost. void DidContentsPreferredWidthChange(const int pref_width); @@ -61,14 +63,12 @@ class ExtensionView : public views::NativeViewHost { void ShowIfCompletelyLoaded(); // The running extension instance that we're displaying. - scoped_ptr<ExtensionHost> host_; + // Note that host_ owns view + ExtensionHost* host_; // The browser window that this view is in. Browser* browser_; - // The URL to navigate the host to upon initialization. - GURL content_url_; - // True if we've been initialized. bool initialized_; diff --git a/chrome/browser/extensions/extension_view_unittest.cc b/chrome/browser/extensions/extension_view_unittest.cc index 6b35eac..2f5abf8 100644 --- a/chrome/browser/extensions/extension_view_unittest.cc +++ b/chrome/browser/extensions/extension_view_unittest.cc @@ -35,9 +35,9 @@ class MockExtensionHost : public ExtensionHost { public: MockExtensionHost(Extension* extension, const GURL& url, SiteInstance* instance) - : ExtensionHost(extension, instance, NULL), + : ExtensionHost(extension, instance, url, NULL), got_message_(false) { - CreateRenderView(url, NULL); + CreateRenderView(NULL); MessageLoop::current()->PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask, kAlertTimeoutMs); ui_test_utils::RunMessageLoop(); @@ -113,12 +113,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionViewTest, Index) { // Tests that the ExtensionShelf initializes properly, notices that // an extension loaded and has a view available, and then sets that up // properly. -IN_PROC_BROWSER_TEST_F(ExtensionViewTest, BottomBar) { +IN_PROC_BROWSER_TEST_F(ExtensionViewTest, Shelf) { // When initialized, there are no extension views and the preferred height // should be zero. scoped_ptr<ExtensionShelf> shelf(new ExtensionShelf(browser())); - ASSERT_FALSE(shelf->HasExtensionViews()); - ASSERT_EQ(shelf->GetPreferredSize().height(), 0); + EXPECT_EQ(shelf->GetChildViewCount(), 0); + EXPECT_EQ(shelf->GetPreferredSize().height(), 0); // Get the path to our extension. FilePath path; @@ -135,6 +135,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionViewTest, BottomBar) { // There should now be two extension views and preferred height of the view // should be non-zero. - ASSERT_TRUE(shelf->HasExtensionViews()); - ASSERT_NE(shelf->GetPreferredSize().height(), 0); + EXPECT_EQ(shelf->GetChildViewCount(), 2); + EXPECT_NE(shelf->GetPreferredSize().height(), 0); } |