Index: chrome/browser/extensions/extension_popup_api.cc |
=================================================================== |
--- chrome/browser/extensions/extension_popup_api.cc (revision 47023) |
+++ chrome/browser/extensions/extension_popup_api.cc (working copy) |
@@ -12,6 +12,9 @@ |
#include "chrome/browser/browser.h" |
#include "chrome/browser/browser_window.h" |
#include "chrome/browser/profile.h" |
+#include "chrome/browser/renderer_host/render_view_host.h" |
+#include "chrome/browser/renderer_host/render_view_host_delegate.h" |
+#include "chrome/browser/renderer_host/render_widget_host_view.h" |
#include "chrome/browser/tab_contents/tab_contents.h" |
#include "chrome/common/extensions/extension.h" |
#include "chrome/common/notification_details.h" |
@@ -22,9 +25,6 @@ |
#include "gfx/point.h" |
#if defined(TOOLKIT_VIEWS) |
-#include "chrome/browser/renderer_host/render_view_host.h" |
-#include "chrome/browser/renderer_host/render_view_host_delegate.h" |
-#include "chrome/browser/renderer_host/render_widget_host_view.h" |
#include "chrome/browser/views/extensions/extension_popup.h" |
#include "views/view.h" |
#include "views/focus/focus_manager.h" |
@@ -42,6 +42,8 @@ |
const char kBadAnchorArgument[] = "Invalid anchor argument."; |
const char kInvalidURLError[] = "Invalid URL."; |
const char kNotAnExtension[] = "Not an extension view."; |
+const char kPopupsDisallowed[] = |
+ "Popups are only supported from toolstrip or tab-contents views."; |
// Keys. |
const wchar_t kUrlKey[] = L"url"; |
@@ -70,7 +72,8 @@ |
// containing view or any of *its* children. |
class ExtensionPopupHost : public ExtensionPopup::Observer, |
public views::WidgetFocusChangeListener, |
- public base::RefCounted<ExtensionPopupHost> { |
+ public base::RefCounted<ExtensionPopupHost>, |
+ public NotificationObserver { |
public: |
explicit ExtensionPopupHost(ExtensionFunctionDispatcher* dispatcher) |
: dispatcher_(dispatcher), popup_(NULL) { |
@@ -85,9 +88,18 @@ |
void set_popup(ExtensionPopup* popup) { |
popup_ = popup; |
+ |
+ // Now that a popup has been assigned, listen for subsequent popups being |
+ // created in the same extension - we want to disallow more than one |
+ // concurrently displayed popup windows. |
+ registrar_.Add( |
+ this, |
+ NotificationType::EXTENSION_HOST_CREATED, |
+ Source<ExtensionProcessManager>( |
+ dispatcher_->profile()->GetExtensionProcessManager())); |
} |
- // Overriden from ExtensionPopup::Observer |
+ // Overridden from ExtensionPopup::Observer |
virtual void ExtensionPopupClosed(ExtensionPopup* popup) { |
// Unregister the automation resource routing registered upon host |
// creation. |
@@ -119,9 +131,13 @@ |
Release(); // Balanced in ctor. |
} |
- // Overriden from views::WidgetFocusChangeListener |
+ // Overridden from views::WidgetFocusChangeListener |
virtual void NativeFocusWillChange(gfx::NativeView focused_before, |
gfx::NativeView focused_now) { |
+ // If the popup doesn't exist, then do nothing. |
+ if (!popup_) |
+ return; |
+ |
// If no view is to be focused, then Chrome was deactivated, so hide the |
// popup. |
if (focused_now) { |
@@ -130,10 +146,13 @@ |
// If the widget hosting the popup contains the newly focused view, then |
// don't dismiss the pop-up. |
- views::Widget* popup_root_widget = popup_->host()->view()->GetWidget(); |
- if (popup_root_widget && |
- popup_root_widget->ContainsNativeView(focused_now)) |
- return; |
+ ExtensionView* view = popup_->host()->view(); |
+ if (view) { |
+ views::Widget* popup_root_widget = view->GetWidget(); |
+ if (popup_root_widget && |
+ popup_root_widget->ContainsNativeView(focused_now)) |
+ return; |
+ } |
// If the widget or RenderWidgetHostView hosting the extension that |
// launched the pop-up is receiving focus, then don't dismiss the popup. |
@@ -156,6 +175,23 @@ |
&ExtensionPopup::Close)); |
} |
+ // Overridden from NotificationObserver |
+ virtual void Observe(NotificationType type, |
+ const NotificationSource& source, |
+ const NotificationDetails& details) { |
+ DCHECK(NotificationType::EXTENSION_HOST_CREATED == type); |
+ if (NotificationType::EXTENSION_HOST_CREATED == type) { |
+ Details<ExtensionHost> details_host(details); |
+ // Disallow multiple pop-ups from the same extension, by closing |
+ // the presently opened popup during construction of any new popups. |
+ if (ViewType::EXTENSION_POPUP == details_host->GetRenderViewType() && |
+ popup_->host()->extension() == details_host->extension() && |
+ Details<ExtensionHost>(popup_->host()) != details) { |
+ popup_->Close(); |
+ } |
+ } |
+ } |
+ |
private: |
// Returns the AutomationResourceRoutingDelegate interface for |dispatcher|. |
static AutomationResourceRoutingDelegate* |
@@ -177,6 +213,8 @@ |
// A pointer to the popup. |
ExtensionPopup* popup_; |
+ NotificationRegistrar registrar_; |
+ |
DISALLOW_COPY_AND_ASSIGN(ExtensionPopupHost); |
}; |
#endif // TOOLKIT_VIEWS |
@@ -211,6 +249,16 @@ |
} |
bool PopupShowFunction::RunImpl() { |
+ // Popups may only be displayed from TAB_CONTENTS and EXTENSION_TOOLSTRIP |
+ // views. |
+ ViewType::Type view_type = |
+ dispatcher()->render_view_host()->delegate()->GetRenderViewType(); |
+ if (ViewType::EXTENSION_TOOLSTRIP != view_type && |
+ ViewType::TAB_CONTENTS != view_type) { |
+ error_ = kPopupsDisallowed; |
+ return false; |
+ } |
+ |
EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST)); |
const ListValue* args = args_as_list(); |
@@ -272,7 +320,6 @@ |
return false; |
} |
-#if defined(TOOLKIT_VIEWS) |
gfx::Point origin(dom_left, dom_top); |
if (!dispatcher()->render_view_host()->view()) { |
error_ = kNotAnExtension; |
@@ -284,14 +331,6 @@ |
origin.Offset(content_bounds.x(), content_bounds.y()); |
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. |
- ViewType::Type view_type = |
- dispatcher()->render_view_host()->delegate()->GetRenderViewType(); |
- BubbleBorder::ArrowLocation arrow_location = |
- view_type == ViewType::TAB_CONTENTS ? |
- BubbleBorder::TOP_LEFT : BubbleBorder::BOTTOM_LEFT; |
- |
// Get the correct native window to pass to ExtensionPopup. |
// ExtensionFunctionDispatcher::Delegate may provide a custom implementation |
// of this. |
@@ -300,6 +339,13 @@ |
if (!window) |
window = GetCurrentBrowser()->window()->GetNativeHandle(); |
+#if defined(TOOLKIT_VIEWS) |
+ // Pop-up from extension views (ExtensionShelf, etc.), and drop-down when |
+ // in a TabContents view. |
+ BubbleBorder::ArrowLocation arrow_location = |
+ view_type == ViewType::TAB_CONTENTS ? |
+ BubbleBorder::TOP_LEFT : BubbleBorder::BOTTOM_LEFT; |
+ |
// ExtensionPopupHost manages it's own lifetime. |
ExtensionPopupHost* popup_host = new ExtensionPopupHost(dispatcher()); |
popup_ = ExtensionPopup::Show(url, |
@@ -329,6 +375,8 @@ |
type == NotificationType::EXTENSION_HOST_DESTROYED); |
DCHECK(popup_ != NULL); |
+ // Wait for notification that the popup view is ready (and onload has been |
+ // called), before completing the API call. |
if (popup_ && type == NotificationType::EXTENSION_POPUP_VIEW_READY && |
Details<ExtensionHost>(popup_->host()) == details) { |
SendResponse(true); |