Refactoring of the chrome.experimental.popup API implementation to allow display of pop-ups for extensions viewed through a tab-contents view.I added a new class, ExtensionPopupHost. This class implements the necessary environment for managing child popup windows from either an ExtensionHost, or an ExtensionDOMUI. Note that this class is added as a member to ExtensionHost and ExtensionDOMUI.
I decided to take this approach to prevent multiple inheritance of the NotificationObserver class: Both ExtensionPopupHost and ExtensionHost must inherit from this class, and I was uncertain of how the system would behave wrt virtual inheritance. Please comment on if I should have used the inheritance approach.
I also removed the customHandler tag (in extension_api.json) that I had added in the initial submission. The arguments in the schema are now those that users of the API see. The nodocs tags were also removed.
The api experimental.popup.getAnchorWindow() has been renamed to popup.getParentWindow, as per a suggestion from Erik K.
BUG=none
TEST=extension_popup_apitest.cc
Review URL: http://codereview.chromium.org/385061
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@32120 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/extension_dom_ui.cc b/chrome/browser/extensions/extension_dom_ui.cc
index 3661d6d..9f118aa 100644
--- a/chrome/browser/extensions/extension_dom_ui.cc
+++ b/chrome/browser/extensions/extension_dom_ui.cc
@@ -218,6 +218,10 @@
}
}
+RenderViewHost* ExtensionDOMUI::GetRenderViewHost() {
+ return tab_contents() ? tab_contents()->render_view_host() : NULL;
+}
+
// static
void ExtensionDOMUI::UnregisterChromeURLOverrides(
Profile* profile, const Extension::URLOverrideMap& overrides) {
diff --git a/chrome/browser/extensions/extension_dom_ui.h b/chrome/browser/extensions/extension_dom_ui.h
index 20549ac..0a53607a 100644
--- a/chrome/browser/extensions/extension_dom_ui.h
+++ b/chrome/browser/extensions/extension_dom_ui.h
@@ -5,19 +5,24 @@
#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_DOM_UI_H_
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_DOM_UI_H_
+#include <string>
+
#include "base/scoped_ptr.h"
#include "chrome/browser/dom_ui/dom_ui.h"
#include "chrome/browser/extensions/extension_function_dispatcher.h"
+#include "chrome/browser/extensions/extension_popup_host.h"
#include "chrome/common/extensions/extension.h"
class ListValue;
class PrefService;
+class RenderViewHost;
class TabContents;
// This class implements DOMUI for extensions and allows extensions to put UI in
// the main tab contents area.
class ExtensionDOMUI
: public DOMUI,
+ public ExtensionPopupHost::PopupDelegate,
public ExtensionFunctionDispatcher::Delegate {
public:
explicit ExtensionDOMUI(TabContents* tab_contents);
@@ -36,6 +41,10 @@
// ExtensionFunctionDispatcher::Delegate
virtual Browser* GetBrowser();
+ virtual ExtensionDOMUI* GetExtensionDOMUI() { return this; }
+
+ // ExtensionPopupHost::Delegate
+ virtual RenderViewHost* GetRenderViewHost();
// BrowserURLHandler
static bool HandleChromeURLOverride(GURL* url, Profile* profile);
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index 1f67979..cb77169b 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -11,6 +11,7 @@
#include "chrome/browser/extensions/extension_bookmarks_module.h"
#include "chrome/browser/extensions/extension_bookmarks_module_constants.h"
#include "chrome/browser/extensions/extension_browser_actions_api.h"
+#include "chrome/browser/extensions/extension_dom_ui.h"
#include "chrome/browser/extensions/extension_function.h"
#include "chrome/browser/extensions/extension_history_api.h"
#include "chrome/browser/extensions/extension_i18n_api.h"
@@ -239,10 +240,26 @@
return delegate_->GetBrowser();
}
+ExtensionPopupHost* ExtensionFunctionDispatcher::GetPopupHost() {
+ ExtensionHost* extension_host = GetExtensionHost();
+ if (extension_host) {
+ DCHECK(!GetExtensionDOMUI()) <<
+ "Function dispatcher registered in too many environments.";
+ return extension_host->popup_host();
+ } else {
+ ExtensionDOMUI* dom_ui = GetExtensionDOMUI();
+ return dom_ui->popup_host();
+ }
+}
+
ExtensionHost* ExtensionFunctionDispatcher::GetExtensionHost() {
return delegate_->GetExtensionHost();
}
+ExtensionDOMUI* ExtensionFunctionDispatcher::GetExtensionDOMUI() {
+ return delegate_->GetExtensionDOMUI();
+}
+
Extension* ExtensionFunctionDispatcher::GetExtension() {
ExtensionsService* service = profile()->GetExtensionsService();
DCHECK(service);
diff --git a/chrome/browser/extensions/extension_function_dispatcher.h b/chrome/browser/extensions/extension_function_dispatcher.h
index d9c0ea58..df50ceb 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.h
+++ b/chrome/browser/extensions/extension_function_dispatcher.h
@@ -14,8 +14,10 @@
class Browser;
class Extension;
+class ExtensionDOMUI;
class ExtensionFunction;
class ExtensionHost;
+class ExtensionPopupHost;
class Profile;
class RenderViewHost;
class RenderViewHostDelegate;
@@ -33,6 +35,7 @@
public:
virtual Browser* GetBrowser() = 0;
virtual ExtensionHost* GetExtensionHost() { return NULL; }
+ virtual ExtensionDOMUI* GetExtensionDOMUI() { return NULL; }
};
// The peer object allows us to notify ExtensionFunctions when we are
@@ -43,10 +46,10 @@
: dispatcher_(dispatcher) {}
ExtensionFunctionDispatcher* dispatcher_;
- private:
- friend class base::RefCounted<Peer>;
+ private:
+ friend class base::RefCounted<Peer>;
- ~Peer() {}
+ ~Peer() {}
};
// Gets a list of all known extension function names.
@@ -79,10 +82,18 @@
// example, for positioning windows, or alert boxes, or creating tabs.
Browser* GetBrowser();
+ // Get the extension popup hosting environment for the ExtensionHost
+ // or ExtensionDOMUI associted with this dispatcher.
+ ExtensionPopupHost* GetPopupHost();
+
// Gets the ExtensionHost associated with this object. In the case of
// tab hosted extension pages, this will return NULL.
ExtensionHost* GetExtensionHost();
+ // Gets the ExtensionDOMUI associated with this object. In the case of
+ // non-tab-hosted extension pages, this will return NULL.
+ ExtensionDOMUI* GetExtensionDOMUI();
+
// Gets the extension the function is being invoked by. This should not ever
// return NULL.
Extension* GetExtension();
diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc
index ba7d54b..c34cb96 100644
--- a/chrome/browser/extensions/extension_host.cc
+++ b/chrome/browser/extensions/extension_host.cc
@@ -20,7 +20,6 @@
#include "chrome/browser/extensions/extension_message_service.h"
#include "chrome/browser/extensions/extension_tabs_module.h"
#include "chrome/browser/jsmessage_box_handler.h"
-#include "chrome/browser/extensions/extension_popup_api.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_process_host.h"
@@ -42,7 +41,6 @@
#include "webkit/glue/context_menu.h"
#if defined(TOOLKIT_VIEWS)
-#include "chrome/browser/views/extensions/extension_popup.h"
#include "views/widget/widget.h"
#endif
@@ -121,25 +119,14 @@
did_stop_loading_(false),
document_element_available_(false),
url_(url),
-#if defined(TOOLKIT_VIEWS)
- child_popup_(NULL),
-#endif
extension_host_type_(host_type) {
render_view_host_ = new RenderViewHost(site_instance, this, MSG_ROUTING_NONE);
render_view_host_->AllowBindings(BindingsPolicy::EXTENSION);
if (enable_dom_automation_)
render_view_host_->AllowBindings(BindingsPolicy::DOM_AUTOMATION);
-
-#if defined(TOOLKIT_VIEWS)
- // Listen for view close requests, so that we can dismiss a hosted pop-up
- // view, if necessary.
- registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE,
- Source<Profile>(profile_));
-#endif
}
ExtensionHost::~ExtensionHost() {
- DismissPopup();
NotificationService::current()->Notify(
NotificationType::EXTENSION_HOST_DESTROYED,
Source<Profile>(profile_),
@@ -229,15 +216,6 @@
NavigateToURL(url_);
} else if (type == NotificationType::BROWSER_THEME_CHANGED) {
InsertThemeCSS();
-#if defined(TOOLKIT_VIEWS)
- } else if (type == NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE) {
- // If we aren't the host of the popup, then disregard the notification.
- if (!child_popup_ ||
- Details<ExtensionHost>(child_popup_->host()) != details)
- return;
-
- DismissPopup();
-#endif
} else {
NOTREACHED();
}
@@ -324,38 +302,6 @@
render_view_host()->InsertCSSInWebFrame(L"", css, "ToolstripThemeCSS");
}
-void ExtensionHost::DismissPopup() {
-#if defined(TOOLKIT_VIEWS)
- if (child_popup_) {
- child_popup_->Hide();
- child_popup_->DetachFromBrowser();
- delete child_popup_;
- child_popup_ = NULL;
-
- PopupEventRouter::OnPopupClosed(GetBrowser()->profile(),
- view()->render_view_host()->routing_id());
- }
-#endif
-}
-
-#if defined(TOOLKIT_VIEWS)
-void ExtensionHost::BubbleBrowserWindowMoved(BrowserBubble* bubble) {
- DismissPopup();
-}
-void ExtensionHost::BubbleBrowserWindowClosing(BrowserBubble* bubble) {
- DismissPopup();
-}
-
-void ExtensionHost::BubbleGotFocus(BrowserBubble* bubble) {
-}
-
-void ExtensionHost::BubbleLostFocus(BrowserBubble* bubble) {
- // TODO(twiz): Dismiss the pop-up upon loss of focus of the bubble, but not
- // if the focus is transitioning to the host which owns the popup!
- // DismissPopup();
-}
-#endif // defined(TOOLKIT_VIEWS)
-
void ExtensionHost::DidStopLoading() {
bool notify = !did_stop_loading_;
did_stop_loading_ = true;
diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h
index e9b414d..9407b85 100644
--- a/chrome/browser/extensions/extension_host.h
+++ b/chrome/browser/extensions/extension_host.h
@@ -10,11 +10,11 @@
#include "base/perftimer.h"
#include "base/scoped_ptr.h"
#include "chrome/browser/extensions/extension_function_dispatcher.h"
+#include "chrome/browser/extensions/extension_popup_host.h"
#include "chrome/browser/jsmessage_box_client.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/views/browser_bubble.h"
#include "chrome/browser/views/extensions/extension_view.h"
#elif defined(OS_LINUX)
#include "chrome/browser/gtk/extension_view_gtk.h"
@@ -26,9 +26,6 @@
class Browser;
class Extension;
-#if defined(TOOLKIT_VIEWS)
-class ExtensionPopup;
-#endif
class ExtensionProcessManager;
class RenderProcessHost;
class RenderWidgetHost;
@@ -40,10 +37,7 @@
// It handles setting up the renderer process, if needed, with special
// privileges available to extensions. It may have a view to be shown in the
// in the browser UI, or it may be hidden.
-class ExtensionHost : // NOLINT
-#if defined(TOOLKIT_VIEWS)
- public BrowserBubble::Delegate,
-#endif
+class ExtensionHost : public ExtensionPopupHost::PopupDelegate,
public RenderViewHostDelegate,
public RenderViewHostDelegate::View,
public ExtensionFunctionDispatcher::Delegate,
@@ -84,14 +78,6 @@
}
Profile* profile() const { return profile_; }
-#if defined(TOOLKIT_VIEWS)
- ExtensionPopup* child_popup() const { return child_popup_; }
- void set_child_popup(ExtensionPopup* popup) { child_popup_ = popup; }
-#endif
-
- // Dismiss the hosted pop-up, if one is present.
- void DismissPopup();
-
// Sets the the ViewType of this host (e.g. mole, toolstrip).
void SetRenderViewType(ViewType::Type type);
@@ -109,22 +95,6 @@
// Insert the theme CSS for a toolstrip/mole.
void InsertThemeCSS();
-#if defined(TOOLKIT_VIEWS)
- // BrowserBubble::Delegate implementation.
- // Called when the Browser Window that this bubble is attached to moves.
- virtual void BubbleBrowserWindowMoved(BrowserBubble* bubble);
-
- // Called with the Browser Window that this bubble is attached to is
- // about to close.
- virtual void BubbleBrowserWindowClosing(BrowserBubble* bubble);
-
- // Called when the bubble became active / got focus.
- virtual void BubbleGotFocus(BrowserBubble* bubble);
-
- // Called when the bubble became inactive / lost focus.
- virtual void BubbleLostFocus(BrowserBubble* bubble);
-#endif // defined(TOOLKIT_VIEWS)
-
// RenderViewHostDelegate implementation.
virtual RenderViewHostDelegate::View* GetViewDelegate();
virtual const GURL& GetURL() const { return url_; }
@@ -204,6 +174,9 @@
virtual Browser* GetBrowser();
virtual ExtensionHost* GetExtensionHost() { return this; }
+ // ExtensionPopupHost::Delegate
+ virtual RenderViewHost* GetRenderViewHost() { return render_view_host(); }
+
// Returns true if we're hosting a background page.
// This isn't valid until CreateRenderView is called.
bool is_background_page() const { return !view(); }
@@ -238,12 +211,6 @@
// The URL being hosted.
GURL url_;
-#if defined(TOOLKIT_VIEWS)
- // A popup view that is anchored to and owned by this ExtensionHost. However,
- // the popup contains its own separate ExtensionHost
- ExtensionPopup* child_popup_;
-#endif
-
NotificationRegistrar registrar_;
scoped_ptr<ExtensionFunctionDispatcher> extension_function_dispatcher_;
diff --git a/chrome/browser/extensions/extension_popup_api.cc b/chrome/browser/extensions/extension_popup_api.cc
index 26db2e1d..f778e0e 100644
--- a/chrome/browser/extensions/extension_popup_api.cc
+++ b/chrome/browser/extensions/extension_popup_api.cc
@@ -12,10 +12,12 @@
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_source.h"
#include "chrome/common/notification_type.h"
+#include "chrome/browser/extensions/extension_dom_ui.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_message_service.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/profile.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
#if defined(TOOLKIT_VIEWS)
#include "chrome/browser/views/extensions/extension_popup.h"
#include "views/view.h"
@@ -32,6 +34,7 @@
// Errors.
const char kBadAnchorArgument[] = "Invalid anchor argument.";
const char kInvalidURLError[] = "Invalid URL.";
+const char kNotAnExtension[] = "Not an extension view.";
// Keys.
const wchar_t kUrlKey[] = L"url";
@@ -111,17 +114,62 @@
}
#if defined(TOOLKIT_VIEWS)
- views::View* extension_view = dispatcher()->GetExtensionHost()->view();
gfx::Point origin(dom_left, dom_top);
- views::View::ConvertPointToScreen(extension_view, &origin);
+ if (!ConvertHostPointToScreen(&origin)) {
+ error_ = kNotAnExtension;
+ return false;
+ }
gfx::Rect rect(origin.x(), origin.y(), dom_width, dom_height);
+ // Pop-up from extension views (ExtensionShelf, etc.), and drop-down when
+ // in a TabContents view.
+ BubbleBorder::ArrowLocation arrow_location =
+ (NULL != dispatcher()->GetExtensionHost()) ? BubbleBorder::BOTTOM_LEFT :
+ BubbleBorder::TOP_LEFT;
popup_ = ExtensionPopup::Show(url, dispatcher()->GetBrowser(), rect,
- BubbleBorder::BOTTOM_LEFT);
+ arrow_location);
- dispatcher()->GetExtensionHost()->set_child_popup(popup_);
- popup_->set_delegate(dispatcher()->GetExtensionHost());
-#endif
+ ExtensionPopupHost* popup_host = dispatcher()->GetPopupHost();
+ DCHECK(popup_host);
+
+ popup_host->set_child_popup(popup_);
+ popup_->set_delegate(popup_host);
+#endif // defined(TOOLKIT_VIEWS)
+ return true;
+}
+
+bool PopupShowFunction::ConvertHostPointToScreen(gfx::Point* point) {
+ DCHECK(point);
+
+ // If the popup is being requested from an ExtensionHost, then compute
+ // the sreen coordinates based on the views::View object of the ExtensionHost.
+ if (dispatcher()->GetExtensionHost()) {
+ // A dispatcher cannot have both an ExtensionHost, and an ExtensionDOMUI.
+ DCHECK(!dispatcher()->GetExtensionDOMUI());
+
+#if defined(TOOLKIT_VIEWS)
+ views::View* extension_view = dispatcher()->GetExtensionHost()->view();
+ if (!extension_view)
+ return false;
+
+ views::View::ConvertPointToScreen(extension_view, point);
+#else
+ // TODO(port)
+ NOTIMPLEMENTED();
+#endif // defined(TOOLKIT_VIEWS)
+ } else if (dispatcher()->GetExtensionDOMUI()) {
+ // Otherwise, the popup is being requested from a TabContents, so determine
+ // the screen-space position through the TabContentsView.
+ ExtensionDOMUI* dom_ui = dispatcher()->GetExtensionDOMUI();
+ TabContents* tab_contents = dom_ui->tab_contents();
+ if (!tab_contents)
+ return false;
+
+ gfx::Rect content_bounds;
+ tab_contents->GetContainerBounds(&content_bounds);
+ point->Offset(content_bounds.x(), content_bounds.y());
+ }
+
return true;
}
@@ -144,7 +192,7 @@
SendResponse(false);
Release(); // Balanced in Run().
}
-#endif
+#endif // defined(TOOLKIT_VIEWS)
}
// static
diff --git a/chrome/browser/extensions/extension_popup_api.h b/chrome/browser/extensions/extension_popup_api.h
index 96d6d53..9132de1 100644
--- a/chrome/browser/extensions/extension_popup_api.h
+++ b/chrome/browser/extensions/extension_popup_api.h
@@ -8,6 +8,10 @@
#include "chrome/browser/extensions/extension_function.h"
#include "chrome/common/notification_registrar.h"
+namespace gfx {
+class Point;
+} // namespace gfx
+
class Profile;
class ExtensionPopup;
@@ -24,6 +28,10 @@
DECLARE_EXTENSION_FUNCTION_NAME("experimental.popup.show")
private:
+ // Computes the screen-space position of the frame-relative point in the
+ // extension view that is requesting to display a popup.
+ bool ConvertHostPointToScreen(gfx::Point* point);
+
// NotificationObserver methods.
virtual void Observe(NotificationType type,
const NotificationSource& source,
diff --git a/chrome/browser/extensions/extension_popup_host.cc b/chrome/browser/extensions/extension_popup_host.cc
new file mode 100644
index 0000000..20dd1a1
--- /dev/null
+++ b/chrome/browser/extensions/extension_popup_host.cc
@@ -0,0 +1,95 @@
+// 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_popup_host.h"
+
+#if defined(TOOLKIT_VIEWS)
+#include "chrome/browser/extensions/extension_popup_api.h"
+#endif
+#include "chrome/browser/profile.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#if defined(TOOLKIT_VIEWS)
+#include "chrome/browser/views/extensions/extension_popup.h"
+#endif
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+
+
+ExtensionPopupHost* ExtensionPopupHost::PopupDelegate::popup_host() {
+ if (!popup_host_.get())
+ popup_host_.reset(new ExtensionPopupHost(this));
+
+ return popup_host_.get();
+}
+
+ExtensionPopupHost::ExtensionPopupHost(PopupDelegate* delegate)
+ : // NO LINT
+#if defined(TOOLKIT_VIEWS)
+ child_popup_(NULL),
+#endif
+ delegate_(delegate) {
+ DCHECK(delegate_);
+
+ // Listen for view close requests, so that we can dismiss a hosted pop-up
+ // view, if necessary.
+ registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE,
+ Source<Profile>(delegate_->GetBrowser()->profile()));
+}
+
+ExtensionPopupHost::~ExtensionPopupHost() {
+ DismissPopup();
+}
+
+#if defined(TOOLKIT_VIEWS)
+void ExtensionPopupHost::BubbleBrowserWindowMoved(BrowserBubble* bubble) {
+ DismissPopup();
+}
+
+void ExtensionPopupHost::BubbleBrowserWindowClosing(BrowserBubble* bubble) {
+ DismissPopup();
+}
+
+void ExtensionPopupHost::BubbleGotFocus(BrowserBubble* bubble) {
+}
+
+void ExtensionPopupHost::BubbleLostFocus(BrowserBubble* bubble) {
+ // TODO(twiz): Dismiss the pop-up upon loss of focus of the bubble, but not
+ // if the focus is transitioning to the host which owns the popup!
+ // DismissPopup();
+}
+#endif // defined(TOOLKIT_VIEWS)
+
+void ExtensionPopupHost::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE) {
+#if defined(TOOLKIT_VIEWS)
+ // If we aren't the host of the popup, then disregard the notification.
+ if (!child_popup_ ||
+ Details<ExtensionHost>(child_popup_->host()) != details) {
+ return;
+ }
+ DismissPopup();
+#endif
+ } else {
+ NOTREACHED();
+ }
+}
+
+void ExtensionPopupHost::DismissPopup() {
+#if defined(TOOLKIT_VIEWS)
+ if (child_popup_) {
+ child_popup_->Hide();
+ child_popup_->DetachFromBrowser();
+ delete child_popup_;
+ child_popup_ = NULL;
+
+ PopupEventRouter::OnPopupClosed(
+ delegate_->GetBrowser()->profile(),
+ delegate_->GetRenderViewHost()->routing_id());
+ }
+#endif // defined(TOOLKIT_VIEWS)
+}
diff --git a/chrome/browser/extensions/extension_popup_host.h b/chrome/browser/extensions/extension_popup_host.h
new file mode 100644
index 0000000..4e38470
--- /dev/null
+++ b/chrome/browser/extensions/extension_popup_host.h
@@ -0,0 +1,104 @@
+// 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_POPUP_HOST_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_POPUP_HOST_H_
+
+#include "base/scoped_ptr.h"
+#include "build/build_config.h"
+#if defined(TOOLKIT_VIEWS)
+#include "chrome/browser/views/browser_bubble.h"
+#endif
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+
+#if defined(TOOLKIT_VIEWS)
+class ExtensionPopup;
+#endif
+
+class Browser;
+class Profile;
+class RenderViewHost;
+
+// ExtensionPopupHost objects implement the environment necessary to host
+// ExtensionPopup views. This class manages the creation and life-time
+// of extension pop-up views.
+class ExtensionPopupHost : // NOLINT
+#if defined(TOOLKIT_VIEWS)
+ public BrowserBubble::Delegate,
+#endif
+ public NotificationObserver {
+ public:
+ // Classes wishing to host pop-ups should inherit from this class, and
+ // implement the virtual methods below. This class manages the lifetime
+ // of an ExtensionPopupHost instance.
+ class PopupDelegate {
+ public:
+ PopupDelegate() {}
+ virtual ~PopupDelegate() {}
+ virtual Browser* GetBrowser() = 0;
+ virtual RenderViewHost* GetRenderViewHost() = 0;
+
+ // Constructs, or returns the existing ExtensionPopupHost instance.
+ ExtensionPopupHost* popup_host();
+ private:
+ scoped_ptr<ExtensionPopupHost> popup_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(PopupDelegate);
+ };
+
+ explicit ExtensionPopupHost(PopupDelegate* delegate);
+ virtual ~ExtensionPopupHost();
+
+ // Dismiss the hosted pop-up, if one is present.
+ void DismissPopup();
+
+#if defined(TOOLKIT_VIEWS)
+ ExtensionPopup* child_popup() const { return child_popup_; }
+ void set_child_popup(ExtensionPopup* popup) {
+ // An extension may only have one popup active at a given time.
+ DismissPopup();
+ child_popup_ = popup;
+ }
+
+ // BrowserBubble::Delegate implementation.
+ // Called when the Browser Window that this bubble is attached to moves.
+ virtual void BubbleBrowserWindowMoved(BrowserBubble* bubble);
+
+ // Called with the Browser Window that this bubble is attached to is
+ // about to close.
+ virtual void BubbleBrowserWindowClosing(BrowserBubble* bubble);
+
+ // Called when the bubble became active / got focus.
+ virtual void BubbleGotFocus(BrowserBubble* bubble);
+
+ // Called when the bubble became inactive / lost focus.
+ virtual void BubbleLostFocus(BrowserBubble* bubble);
+#endif // defined(TOOLKIT_VIEWS)
+
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+#if defined(TOOLKIT_VIEWS)
+ // A popup view that is anchored to and owned by this ExtensionHost. However,
+ // the popup contains its own separate ExtensionHost
+ ExtensionPopup* child_popup_;
+#endif
+
+ NotificationRegistrar registrar_;
+
+ // Non-owning pointer to the delegate for this host.
+ PopupDelegate* delegate_;
+
+ // Boolean value used to ensure that the host only registers for event
+ // notifications once.
+ bool listeners_registered_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionPopupHost);
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_POPUP_HOST_H_