summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/background_contents_service.h1
-rw-r--r--chrome/browser/extensions/extension_host.cc17
-rw-r--r--chrome/browser/extensions/extension_host.h9
-rw-r--r--chrome/browser/extensions/extension_host_mac.mm3
-rw-r--r--chrome/browser/notifications/balloon_host.h7
-rw-r--r--chrome/browser/renderer_host/render_view_host.cc28
-rw-r--r--chrome/browser/renderer_host/render_view_host.h10
-rw-r--r--chrome/browser/renderer_host/render_view_host_delegate.h11
-rw-r--r--chrome/browser/renderer_host/render_widget_host.cc11
-rw-r--r--chrome/browser/renderer_host/render_widget_host.h1
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view.h9
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_mac.h22
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_mac.mm119
-rw-r--r--chrome/browser/renderer_host/test/test_render_view_host.h1
-rw-r--r--chrome/browser/tab_contents/background_contents.h7
-rw-r--r--chrome/browser/tab_contents/interstitial_page.cc20
-rw-r--r--chrome/browser/tab_contents/popup_menu_helper_mac.h46
-rw-r--r--chrome/browser/tab_contents/popup_menu_helper_mac.mm87
-rw-r--r--chrome/browser/tab_contents/tab_contents_view_gtk.cc11
-rw-r--r--chrome/browser/tab_contents/tab_contents_view_gtk.h8
-rw-r--r--chrome/browser/tab_contents/tab_contents_view_mac.h7
-rw-r--r--chrome/browser/tab_contents/tab_contents_view_mac.mm17
-rw-r--r--chrome/browser/views/tab_contents/tab_contents_view_gtk.cc10
-rw-r--r--chrome/browser/views/tab_contents/tab_contents_view_gtk.h6
-rw-r--r--chrome/browser/views/tab_contents/tab_contents_view_win.cc10
-rw-r--r--chrome/browser/views/tab_contents/tab_contents_view_win.h6
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_renderer.gypi2
-rw-r--r--chrome/chrome_tests.gypi3
-rw-r--r--chrome/common/render_messages_internal.h4
-rw-r--r--chrome/renderer/external_popup_menu.cc47
-rw-r--r--chrome/renderer/external_popup_menu.h40
-rw-r--r--chrome/renderer/external_popup_menu_unittest.cc98
-rw-r--r--chrome/renderer/render_view.cc22
-rw-r--r--chrome/renderer/render_view.h14
-rw-r--r--chrome/renderer/renderer_main.cc4
-rw-r--r--chrome/test/render_view_test.cc14
-rw-r--r--chrome/test/render_view_test.h6
38 files changed, 583 insertions, 157 deletions
diff --git a/chrome/browser/background_contents_service.h b/chrome/browser/background_contents_service.h
index e9d7df0a..08165d8 100644
--- a/chrome/browser/background_contents_service.h
+++ b/chrome/browser/background_contents_service.h
@@ -16,7 +16,6 @@
#include "googleurl/src/gurl.h"
#include "webkit/glue/window_open_disposition.h"
-class BackgroundContents;
class CommandLine;
class PrefService;
class Profile;
diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc
index b35d58c..4de1a5d 100644
--- a/chrome/browser/extensions/extension_host.cc
+++ b/chrome/browser/extensions/extension_host.cc
@@ -34,6 +34,7 @@
#include "chrome/browser/renderer_host/render_widget_host_view.h"
#include "chrome/browser/renderer_host/site_instance.h"
#include "chrome/browser/renderer_preferences_util.h"
+#include "chrome/browser/tab_contents/popup_menu_helper_mac.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
#include "chrome/browser/themes/browser_theme_provider.h"
@@ -621,6 +622,22 @@ void ExtensionHost::ShowContextMenu(const ContextMenuParams& params) {
// TODO(erikkay) Show a default context menu.
}
+void ExtensionHost::ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) {
+#if defined(OS_MACOSX)
+ PopupMenuHelper popup_menu_helper(render_view_host());
+ popup_menu_helper.ShowPopupMenu(bounds, item_height, item_font_size,
+ selected_item, items, right_aligned);
+#else
+ // Only on Mac are select popup menus external.
+ NOTREACHED();
+#endif
+}
+
void ExtensionHost::StartDragging(const WebDropData& drop_data,
WebDragOperationsMask operation_mask,
const SkBitmap& image,
diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h
index 49b8191..cf677e3 100644
--- a/chrome/browser/extensions/extension_host.h
+++ b/chrome/browser/extensions/extension_host.h
@@ -6,8 +6,9 @@
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_HOST_H_
#pragma once
-#include <string>
#include <list>
+#include <string>
+#include <vector>
#include "base/perftimer.h"
#include "base/scoped_ptr.h"
@@ -155,6 +156,12 @@ class ExtensionHost : public RenderViewHostDelegate,
const gfx::Rect& initial_pos);
virtual void ShowCreatedFullscreenWidget(int route_id);
virtual void ShowContextMenu(const ContextMenuParams& params);
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned);
virtual void StartDragging(const WebDropData& drop_data,
WebKit::WebDragOperationsMask allowed_operations,
const SkBitmap& image,
diff --git a/chrome/browser/extensions/extension_host_mac.mm b/chrome/browser/extensions/extension_host_mac.mm
index 3239ebd..d6de067 100644
--- a/chrome/browser/extensions/extension_host_mac.mm
+++ b/chrome/browser/extensions/extension_host_mac.mm
@@ -31,9 +31,6 @@ RenderWidgetHostView* ExtensionHostMac::CreateNewWidgetInternal(
static_cast<RenderWidgetHostViewMac*>(widget_view);
[widget_view_mac->native_view() retain];
- // |widget_view_mac| needs to know how to position itself in our view.
- widget_view_mac->set_parent_view(view()->native_view());
-
return widget_view;
}
diff --git a/chrome/browser/notifications/balloon_host.h b/chrome/browser/notifications/balloon_host.h
index 1c8b088..09d54ea 100644
--- a/chrome/browser/notifications/balloon_host.h
+++ b/chrome/browser/notifications/balloon_host.h
@@ -7,6 +7,7 @@
#pragma once
#include <string>
+#include <vector>
#include "chrome/browser/extensions/extension_function_dispatcher.h"
#include "chrome/browser/notifications/balloon.h"
@@ -77,6 +78,12 @@ class BalloonHost : public RenderViewHostDelegate,
const gfx::Rect& initial_pos) {}
virtual void ShowCreatedFullscreenWidget(int route_id) {}
virtual void ShowContextMenu(const ContextMenuParams& params) {}
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) {}
virtual void StartDragging(const WebDropData& drop_data,
WebKit::WebDragOperationsMask allowed_ops) {}
virtual void StartDragging(const WebDropData&,
diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc
index 68a0fd8..5d65bff 100644
--- a/chrome/browser/renderer_host/render_view_host.cc
+++ b/chrome/browser/renderer_host/render_view_host.cc
@@ -898,6 +898,9 @@ void RenderViewHost::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(ViewHostMsg_ScriptEvalResponse, OnScriptEvalResponse)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateContentRestrictions,
OnUpdateContentRestrictions)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ShowPopup, OnMsgShowPopup)
+#endif
#if defined(OS_MACOSX) || defined(OS_WIN)
IPC_MESSAGE_HANDLER(ViewHostMsg_PageReadyForPreview,
OnPageReadyForPreview)
@@ -2024,6 +2027,16 @@ void RenderViewHost::EnablePreferredSizeChangedMode(int flags) {
Send(new ViewMsg_EnablePreferredSizeChangedMode(routing_id(), flags));
}
+#if defined(OS_MACOSX)
+void RenderViewHost::DidSelectPopupMenuItem(int selected_index) {
+ Send(new ViewMsg_SelectPopupMenuItem(routing_id(), selected_index));
+}
+
+void RenderViewHost::DidCancelPopupMenu() {
+ Send(new ViewMsg_SelectPopupMenuItem(routing_id(), -1));
+}
+#endif
+
void RenderViewHost::OnExtensionPostMessage(
int port_id, const std::string& message) {
if (process()->profile()->GetExtensionMessageService()) {
@@ -2155,6 +2168,21 @@ void RenderViewHost::OnUpdateContentRestrictions(int restrictions) {
delegate_->UpdateContentRestrictions(restrictions);
}
+#if defined(OS_MACOSX)
+void RenderViewHost::OnMsgShowPopup(
+ const ViewHostMsg_ShowPopup_Params& params) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view) {
+ view->ShowPopupMenu(params.bounds,
+ params.item_height,
+ params.item_font_size,
+ params.selected_item,
+ params.popup_items,
+ params.right_aligned);
+ }
+}
+#endif
+
#if defined(OS_MACOSX) || defined(OS_WIN)
void RenderViewHost::OnPageReadyForPreview(
const ViewHostMsg_DidPrintPage_Params& params) {
diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h
index e8a6150..00fd2004 100644
--- a/chrome/browser/renderer_host/render_view_host.h
+++ b/chrome/browser/renderer_host/render_view_host.h
@@ -496,6 +496,12 @@ class RenderViewHost : public RenderWidgetHost {
// in render_messages.h.
void EnablePreferredSizeChangedMode(int flags);
+#if defined(OS_MACOSX)
+ // Select popup menu related methods (for external popup menus).
+ void DidSelectPopupMenuItem(int selected_index);
+ void DidCancelPopupMenu();
+#endif
+
#if defined(UNIT_TEST)
// These functions shouldn't be necessary outside of testing.
@@ -712,6 +718,10 @@ class RenderViewHost : public RenderWidgetHost {
void OnPagesReadyForPreview(int fd_in_browser);
#endif
+#if defined(OS_MACOSX)
+ void OnMsgShowPopup(const ViewHostMsg_ShowPopup_Params& params);
+#endif
+
private:
friend class TestRenderViewHost;
diff --git a/chrome/browser/renderer_host/render_view_host_delegate.h b/chrome/browser/renderer_host/render_view_host_delegate.h
index d0c88d6..5332a83 100644
--- a/chrome/browser/renderer_host/render_view_host_delegate.h
+++ b/chrome/browser/renderer_host/render_view_host_delegate.h
@@ -50,6 +50,7 @@ struct ViewHostMsg_FrameNavigate_Params;
struct ViewHostMsg_PageHasOSDD_Type;
struct ViewHostMsg_RunFileChooser_Params;
struct WebDropData;
+struct WebMenuItem;
class WebKeyboardEvent;
struct WebPreferences;
@@ -146,6 +147,16 @@ class RenderViewHostDelegate {
// provided in the supplied params.
virtual void ShowContextMenu(const ContextMenuParams& params) = 0;
+ // Shows a popup menu with the specified items.
+ // This method should call RenderViewHost::DidSelectPopupMenuItemAt() or
+ // RenderViewHost::DidCancelPopupMenu() ased on the user action.
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) = 0;
+
// The user started dragging content of the specified type within the
// RenderView. Contextual information about the dragged content is supplied
// by WebDropData.
diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc
index a6efd58..e426041 100644
--- a/chrome/browser/renderer_host/render_widget_host.cc
+++ b/chrome/browser/renderer_host/render_widget_host.cc
@@ -164,7 +164,6 @@ void RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) {
IPC_MESSAGE_HANDLER(ViewHostMsg_GpuRenderingActivated,
OnMsgGpuRenderingActivated)
#if defined(OS_MACOSX)
- IPC_MESSAGE_HANDLER(ViewHostMsg_ShowPopup, OnMsgShowPopup)
IPC_MESSAGE_HANDLER(ViewHostMsg_GetScreenInfo, OnMsgGetScreenInfo)
IPC_MESSAGE_HANDLER(ViewHostMsg_GetWindowRect, OnMsgGetWindowRect)
IPC_MESSAGE_HANDLER(ViewHostMsg_GetRootWindowRect, OnMsgGetRootWindowRect)
@@ -978,16 +977,6 @@ void RenderWidgetHost::OnMsgGpuRenderingActivated(bool activated) {
#if defined(OS_MACOSX)
-void RenderWidgetHost::OnMsgShowPopup(
- const ViewHostMsg_ShowPopup_Params& params) {
- view_->ShowPopupWithItems(params.bounds,
- params.item_height,
- params.item_font_size,
- params.selected_item,
- params.popup_items,
- params.right_aligned);
-}
-
void RenderWidgetHost::OnMsgGetScreenInfo(gfx::NativeViewId view,
WebScreenInfo* results) {
gfx::NativeView native_view = view_ ? view_->GetNativeView() : NULL;
diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h
index 6c3fed7..a914a40 100644
--- a/chrome/browser/renderer_host/render_widget_host.h
+++ b/chrome/browser/renderer_host/render_widget_host.h
@@ -494,7 +494,6 @@ class RenderWidgetHost : public IPC::Channel::Listener,
void OnMsgGpuRenderingActivated(bool activated);
#if defined(OS_MACOSX)
- void OnMsgShowPopup(const ViewHostMsg_ShowPopup_Params& params);
void OnMsgGetScreenInfo(gfx::NativeViewId view,
WebKit::WebScreenInfo* results);
void OnMsgGetWindowRect(gfx::NativeViewId window_id, gfx::Rect* results);
diff --git a/chrome/browser/renderer_host/render_widget_host_view.h b/chrome/browser/renderer_host/render_widget_host_view.h
index 46d6bda..c13fd70 100644
--- a/chrome/browser/renderer_host/render_widget_host_view.h
+++ b/chrome/browser/renderer_host/render_widget_host_view.h
@@ -34,7 +34,6 @@ class VideoLayer;
class WebCursor;
struct NativeWebKeyboardEvent;
struct ViewHostMsg_AccessibilityNotification_Params;
-struct WebMenuItem;
namespace webkit_glue {
struct WebAccessibility;
@@ -184,14 +183,6 @@ class RenderWidgetHostView {
// |flag| is false, the view participates in the key-view chain as normal.
virtual void SetTakesFocusOnlyOnMouseDown(bool flag) = 0;
- // Display a native control popup menu for WebKit.
- virtual void ShowPopupWithItems(gfx::Rect bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<WebMenuItem>& items,
- bool right_aligned) = 0;
-
// Get the view's position on the screen.
virtual gfx::Rect GetWindowRect() = 0;
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.h b/chrome/browser/renderer_host/render_widget_host_view_mac.h
index 45af5c0..6efa36a 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.h
@@ -21,7 +21,6 @@
#include "chrome/common/edit_command.h"
#include "third_party/WebKit/WebKit/chromium/public/WebCompositionUnderline.h"
#include "webkit/glue/webcursor.h"
-#include "webkit/glue/webmenuitem.h"
@class AcceleratedPluginView;
class RenderWidgetHostViewMac;
@@ -209,12 +208,6 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
virtual BackingStore* AllocBackingStore(const gfx::Size& size);
virtual VideoLayer* AllocVideoLayer(const gfx::Size& size);
virtual void SetTakesFocusOnlyOnMouseDown(bool flag);
- virtual void ShowPopupWithItems(gfx::Rect bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<WebMenuItem>& items,
- bool right_aligned);
virtual gfx::Rect GetWindowRect();
virtual gfx::Rect GetRootWindowRect();
virtual void SetActive(bool active);
@@ -265,8 +258,6 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
void KillSelf();
- void set_parent_view(NSView* parent_view) { parent_view_ = parent_view; }
-
void SetTextInputActive(bool active);
// Sends confirmed plugin IME text back to the renderer.
@@ -328,11 +319,7 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
bool IsVoiceOverRunning();
// The associated view. This is weak and is inserted into the view hierarchy
- // to own this RenderWidgetHostViewMac object unless is_popup_menu_ is true.
- // In that case, cocoa_view_ is never inserted into the view hierarchy, so
- // the RenderWidgetHostViewMac will treat it as a strong reference and will
- // release it when told to destroy (for example, because a pop-up menu has
- // closed).
+ // to own this RenderWidgetHostViewMac object.
RenderWidgetHostViewCocoa* cocoa_view_;
// The cursor for the page. This is passed up from the renderer.
@@ -344,19 +331,12 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
// true if the View is not visible.
bool is_hidden_;
- // True if the widget is a native popup menu. The renderer code calls this
- // an "external popup."
- bool is_popup_menu_;
-
// The text to be shown in the tooltip, supplied by the renderer.
std::wstring tooltip_text_;
// Factory used to safely scope delayed calls to ShutdownHost().
ScopedRunnableMethodFactory<RenderWidgetHostViewMac> shutdown_factory_;
- // Used for positioning a popup menu.
- NSView* parent_view_;
-
// selected text on the renderer.
std::string selected_text_;
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
index 4c06e20..9e5c6e7 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
@@ -41,7 +41,6 @@
#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
#include "webkit/glue/plugins/webplugin.h"
#include "webkit/glue/webaccessibility.h"
-#include "webkit/glue/webmenurunner_mac.h"
#import "third_party/mozilla/ComplexTextInputPanel.h"
using WebKit::WebInputEvent;
@@ -438,9 +437,7 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
text_input_type_(WebKit::WebTextInputTypeNone),
is_loading_(false),
is_hidden_(false),
- is_popup_menu_(false),
- shutdown_factory_(this),
- parent_view_(NULL) {
+ shutdown_factory_(this) {
// |cocoa_view_| owns us and we will be deleted when |cocoa_view_| goes away.
// Since we autorelease it, our caller must put |native_view()| into the view
// hierarchy right after calling us.
@@ -732,37 +729,23 @@ void RenderWidgetHostViewMac::Destroy() {
// time Destroy() was called. On the Mac we have to destroy all the popups
// ourselves.
- if (!is_popup_menu_) {
- // Depth-first destroy all popups. Use ShutdownHost() to enforce
- // deepest-first ordering.
- for (NSView* subview in [cocoa_view_ subviews]) {
- if ([subview isKindOfClass:[RenderWidgetHostViewCocoa class]]) {
- [static_cast<RenderWidgetHostViewCocoa*>(subview)
- renderWidgetHostViewMac]->ShutdownHost();
- } else if ([subview isKindOfClass:[AcceleratedPluginView class]]) {
- [static_cast<AcceleratedPluginView*>(subview)
- onRenderWidgetHostViewGone];
- }
+ // Depth-first destroy all popups. Use ShutdownHost() to enforce
+ // deepest-first ordering.
+ for (NSView* subview in [cocoa_view_ subviews]) {
+ if ([subview isKindOfClass:[RenderWidgetHostViewCocoa class]]) {
+ [static_cast<RenderWidgetHostViewCocoa*>(subview)
+ renderWidgetHostViewMac]->ShutdownHost();
+ } else if ([subview isKindOfClass:[AcceleratedPluginView class]]) {
+ [static_cast<AcceleratedPluginView*>(subview)
+ onRenderWidgetHostViewGone];
}
-
- // We've been told to destroy.
- [cocoa_view_ retain];
- [cocoa_view_ removeFromSuperview];
- [cocoa_view_ autorelease];
- } else {
- // From the renderer's perspective, the pop-up menu is represented by a
- // RenderWidget. The actual Mac implementation uses a native pop-up menu
- // and doesn't actually make use of the RenderWidgetHostViewCocoa that
- // was allocated to own it in its constructor. When the pop-up menu goes
- // away, free the RenderWidgetHostViewCocoa. Its deallocation will result
- // in this object's destruction.
-
- DCHECK([[cocoa_view_ subviews] count] == 0);
- DCHECK([cocoa_view_ superview] == nil);
-
- [cocoa_view_ autorelease];
}
+ // We've been told to destroy.
+ [cocoa_view_ retain];
+ [cocoa_view_ removeFromSuperview];
+ [cocoa_view_ autorelease];
+
// We get this call just before |render_widget_host_| deletes
// itself. But we are owned by |cocoa_view_|, which may be retained
// by some other code. Examples are TabContentsViewMac's
@@ -815,78 +798,6 @@ void RenderWidgetHostViewMac::SetTakesFocusOnlyOnMouseDown(bool flag) {
[cocoa_view_ setTakesFocusOnlyOnMouseDown:flag];
}
-// Display a popup menu for WebKit using Cocoa widgets.
-void RenderWidgetHostViewMac::ShowPopupWithItems(
- gfx::Rect bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<WebMenuItem>& items,
- bool right_aligned) {
- is_popup_menu_ = true;
-
- // Retain the Cocoa view for the duration of the pop-up so that it can't
- // be dealloced if my Destroy() method is called while the pop-up's up
- // (which would in turn delete me, causing a crash once the -runMenuInView
- // call returns. That's what was happening in <http://crbug.com/33250>).
- scoped_nsobject<RenderWidgetHostViewCocoa> retainedCocoaView
- ([cocoa_view_ retain]);
-
- NSRect view_rect = [cocoa_view_ bounds];
- NSRect parent_rect = [parent_view_ bounds];
- int y_offset = bounds.y() + bounds.height();
- NSRect position = NSMakeRect(bounds.x(), parent_rect.size.height - y_offset,
- bounds.width(), bounds.height());
-
- // Display the menu.
- scoped_nsobject<WebMenuRunner> menu_runner;
- menu_runner.reset([[WebMenuRunner alloc] initWithItems:items
- fontSize:item_font_size
- rightAligned:right_aligned]);
-
- {
- // Make sure events can be pumped while the menu is up.
- MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
-
- // One of the events that could be pumped is |window.close()|.
- // User-initiated event-tracking loops protect against this by
- // setting flags in -[CrApplication sendEvent:], but since
- // web-content menus are initiated by IPC message the setup has to
- // be done manually.
- chrome_application_mac::ScopedSendingEvent sendingEventScoper;
-
- // Now run a SYNCHRONOUS NESTED EVENT LOOP until the pop-up is finished.
- [menu_runner runMenuInView:parent_view_
- withBounds:position
- initialIndex:selected_item];
- }
-
- if (!render_widget_host_) {
- // Bad news -- my Destroy() was called while I was off running the menu.
- // Return ASAP, and the release of retainedCocoaView will dealloc my NSView,
- // which will delete me (whew).
- return;
- }
-
- int window_num = [[parent_view_ window] windowNumber];
- NSEvent* event =
- webkit_glue::EventWithMenuAction([menu_runner menuItemWasChosen],
- window_num, item_height,
- [menu_runner indexOfSelectedItem],
- position, view_rect);
-
- if ([menu_runner menuItemWasChosen]) {
- // Simulate a menu selection event.
- const WebMouseEvent& mouse_event =
- WebInputEventFactory::mouseEvent(event, cocoa_view_);
- render_widget_host_->ForwardMouseEvent(mouse_event);
- } else {
- // Simulate a menu dismiss event.
- NativeWebKeyboardEvent keyboard_event(event);
- render_widget_host_->ForwardKeyboardEvent(keyboard_event);
- }
-}
-
void RenderWidgetHostViewMac::KillSelf() {
if (shutdown_factory_.empty()) {
[cocoa_view_ setHidden:YES];
diff --git a/chrome/browser/renderer_host/test/test_render_view_host.h b/chrome/browser/renderer_host/test/test_render_view_host.h
index e3c9f39..034e100 100644
--- a/chrome/browser/renderer_host/test/test_render_view_host.h
+++ b/chrome/browser/renderer_host/test/test_render_view_host.h
@@ -25,6 +25,7 @@ class NavigationController;
class SiteInstance;
class TestingProfile;
class TestTabContents;
+struct WebMenuItem;
struct ViewHostMsg_FrameNavigate_Params;
// Utility function to initialize ViewHostMsg_NavigateParams_Params
diff --git a/chrome/browser/tab_contents/background_contents.h b/chrome/browser/tab_contents/background_contents.h
index 0e48416..63a30d9 100644
--- a/chrome/browser/tab_contents/background_contents.h
+++ b/chrome/browser/tab_contents/background_contents.h
@@ -7,6 +7,7 @@
#pragma once
#include <string>
+#include <vector>
#include "chrome/browser/js_modal_dialog.h"
#include "chrome/browser/renderer_host/render_view_host_delegate.h"
@@ -90,6 +91,12 @@ class BackgroundContents : public RenderViewHostDelegate,
const gfx::Rect& initial_pos);
virtual void ShowCreatedFullscreenWidget(int route_id);
virtual void ShowContextMenu(const ContextMenuParams& params) {}
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) {}
virtual void StartDragging(const WebDropData& drop_data,
WebKit::WebDragOperationsMask allowed_operations,
const SkBitmap& image,
diff --git a/chrome/browser/tab_contents/interstitial_page.cc b/chrome/browser/tab_contents/interstitial_page.cc
index 8663eef..ae1e078 100644
--- a/chrome/browser/tab_contents/interstitial_page.cc
+++ b/chrome/browser/tab_contents/interstitial_page.cc
@@ -104,6 +104,12 @@ class InterstitialPage::InterstitialPageRVHViewDelegate
const gfx::Rect& initial_pos);
virtual void ShowCreatedFullscreenWidget(int route_id);
virtual void ShowContextMenu(const ContextMenuParams& params);
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned);
virtual void StartDragging(const WebDropData& drop_data,
WebDragOperationsMask operations_allowed,
const SkBitmap& image,
@@ -615,6 +621,15 @@ void InterstitialPage::InterstitialPageRVHViewDelegate::ShowContextMenu(
const ContextMenuParams& params) {
}
+void InterstitialPage::InterstitialPageRVHViewDelegate::ShowPopupMenu(
+ const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) {
+}
+
void InterstitialPage::InterstitialPageRVHViewDelegate::StartDragging(
const WebDropData& drop_data,
WebDragOperationsMask allowed_operations,
@@ -699,8 +714,9 @@ int InterstitialPage::GetBrowserWindowID() const {
}
void InterstitialPage::UpdateInspectorSetting(const std::string& key,
- const std::string& value) {
- RenderViewHostDelegateHelper::UpdateInspectorSetting(tab_->profile(), key, value);
+ const std::string& value) {
+ RenderViewHostDelegateHelper::UpdateInspectorSetting(
+ tab_->profile(), key, value);
}
void InterstitialPage::ClearInspectorSettings() {
diff --git a/chrome/browser/tab_contents/popup_menu_helper_mac.h b/chrome/browser/tab_contents/popup_menu_helper_mac.h
new file mode 100644
index 0000000..08a4c19
--- /dev/null
+++ b/chrome/browser/tab_contents/popup_menu_helper_mac.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2010 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_TAB_CONTENTS_POPUP_MENU_HELPER_MAC_H_
+#define CHROME_BROWSER_TAB_CONTENTS_POPUP_MENU_HELPER_MAC_H_
+
+#include <vector>
+
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "gfx/rect.h"
+
+class RenderViewHost;
+struct WebMenuItem;
+
+class PopupMenuHelper : public NotificationObserver {
+ public:
+ // Creates a PopupMenuHelper that will notify |render_view_host| when a user
+ // selects or cancels the popup.
+ explicit PopupMenuHelper(RenderViewHost* render_view_host);
+
+ // Shows the popup menu and notifies the RenderViewHost of the selection/
+ // cancel.
+ // This call is blocking.
+ void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned);
+
+ private:
+ // NotificationObserver implementation:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ NotificationRegistrar notification_registrar_;
+
+ RenderViewHost* render_view_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(PopupMenuHelper);
+};
+
+#endif // CHROME_BROWSER_TAB_CONTENTS_POPUP_MENU_HELPER_MAC_H_
diff --git a/chrome/browser/tab_contents/popup_menu_helper_mac.mm b/chrome/browser/tab_contents/popup_menu_helper_mac.mm
new file mode 100644
index 0000000..0e910b7
--- /dev/null
+++ b/chrome/browser/tab_contents/popup_menu_helper_mac.mm
@@ -0,0 +1,87 @@
+// Copyright (c) 2010 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.
+
+#import <Carbon/Carbon.h>
+
+#include "chrome/browser/tab_contents/popup_menu_helper_mac.h"
+
+#import "base/chrome_application_mac.h"
+#include "base/message_loop.h"
+#include "base/scoped_nsobject.h"
+#import "chrome/browser/cocoa/base_view.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/renderer_host/render_widget_host_view_mac.h"
+#include "chrome/common/notification_source.h"
+#include "webkit/glue/webmenurunner_mac.h"
+
+PopupMenuHelper::PopupMenuHelper(RenderViewHost* render_view_host)
+ : render_view_host_(render_view_host) {
+ notification_registrar_.Add(
+ this, NotificationType::RENDER_WIDGET_HOST_DESTROYED,
+ Source<RenderWidgetHost>(render_view_host));
+}
+
+void PopupMenuHelper::ShowPopupMenu(
+ const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) {
+ // Retain the Cocoa view for the duration of the pop-up so that it can't be
+ // dealloced if my Destroy() method is called while the pop-up's up (which
+ // would in turn delete me, causing a crash once the -runMenuInView
+ // call returns. That's what was happening in <http://crbug.com/33250>).
+ RenderWidgetHostViewMac* rwhvm =
+ static_cast<RenderWidgetHostViewMac*>(render_view_host_->view());
+ scoped_nsobject<RenderWidgetHostViewCocoa> cocoa_view
+ ([rwhvm->native_view() retain]);
+
+ // Display the menu.
+ scoped_nsobject<WebMenuRunner> menu_runner;
+ menu_runner.reset([[WebMenuRunner alloc] initWithItems:items
+ fontSize:item_font_size
+ rightAligned:right_aligned]);
+
+ {
+ // Make sure events can be pumped while the menu is up.
+ MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
+
+ // One of the events that could be pumped is |window.close()|.
+ // User-initiated event-tracking loops protect against this by
+ // setting flags in -[CrApplication sendEvent:], but since
+ // web-content menus are initiated by IPC message the setup has to
+ // be done manually.
+ chrome_application_mac::ScopedSendingEvent sendingEventScoper;
+
+ // Now run a SYNCHRONOUS NESTED EVENT LOOP until the pop-up is finished.
+ [menu_runner runMenuInView:cocoa_view
+ withBounds:[cocoa_view flipRectToNSRect:bounds]
+ initialIndex:selected_item];
+ }
+
+ if (!render_view_host_) {
+ // Bad news, the RenderViewHost got deleted while we were off running the
+ // menu. Nothing to do.
+ return;
+ }
+
+ if ([menu_runner menuItemWasChosen]) {
+ render_view_host_->DidSelectPopupMenuItem(
+ [menu_runner indexOfSelectedItem]);
+ } else {
+ render_view_host_->DidCancelPopupMenu();
+ }
+}
+
+void PopupMenuHelper::Observe(
+ NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::RENDER_WIDGET_HOST_DESTROYED);
+ RenderViewHost* rvh = Source<RenderViewHost>(source).ptr();
+ DCHECK_EQ(render_view_host_, rvh);
+ render_view_host_ = NULL;
+}
+
diff --git a/chrome/browser/tab_contents/tab_contents_view_gtk.cc b/chrome/browser/tab_contents/tab_contents_view_gtk.cc
index 77f8aad..2c8a276 100644
--- a/chrome/browser/tab_contents/tab_contents_view_gtk.cc
+++ b/chrome/browser/tab_contents/tab_contents_view_gtk.cc
@@ -311,6 +311,17 @@ void TabContentsViewGtk::ShowContextMenu(const ContextMenuParams& params) {
context_menu_->Popup(point);
}
+void TabContentsViewGtk::ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) {
+ // We are not using external popup menus on Linux, they are rendered by
+ // WebKit.
+ NOTREACHED();
+}
+
// Render view DnD -------------------------------------------------------------
void TabContentsViewGtk::StartDragging(const WebDropData& drop_data,
diff --git a/chrome/browser/tab_contents/tab_contents_view_gtk.h b/chrome/browser/tab_contents/tab_contents_view_gtk.h
index 6f92a9b..2ea02ca 100644
--- a/chrome/browser/tab_contents/tab_contents_view_gtk.h
+++ b/chrome/browser/tab_contents/tab_contents_view_gtk.h
@@ -8,6 +8,8 @@
#include <gtk/gtk.h>
+#include <vector>
+
#include "app/gtk_signal.h"
#include "base/scoped_ptr.h"
#include "chrome/browser/gtk/focus_store_gtk.h"
@@ -61,6 +63,12 @@ class TabContentsViewGtk : public TabContentsView,
// Backend implementation of RenderViewHostDelegate::View.
virtual void ShowContextMenu(const ContextMenuParams& params);
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned);
virtual void StartDragging(const WebDropData& drop_data,
WebKit::WebDragOperationsMask allowed_ops,
const SkBitmap& image,
diff --git a/chrome/browser/tab_contents/tab_contents_view_mac.h b/chrome/browser/tab_contents/tab_contents_view_mac.h
index 63d29f3..47b88cf 100644
--- a/chrome/browser/tab_contents/tab_contents_view_mac.h
+++ b/chrome/browser/tab_contents/tab_contents_view_mac.h
@@ -9,6 +9,7 @@
#import <Cocoa/Cocoa.h>
#include <string>
+#include <vector>
#include "base/scoped_nsobject.h"
#include "chrome/browser/cocoa/base_view.h"
@@ -76,6 +77,12 @@ class TabContentsViewMac : public TabContentsView,
// Backend implementation of RenderViewHostDelegate::View.
virtual void ShowContextMenu(const ContextMenuParams& params);
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned);
virtual void StartDragging(const WebDropData& drop_data,
WebKit::WebDragOperationsMask allowed_operations,
const SkBitmap& image,
diff --git a/chrome/browser/tab_contents/tab_contents_view_mac.mm b/chrome/browser/tab_contents/tab_contents_view_mac.mm
index eb550ab..acc9813 100644
--- a/chrome/browser/tab_contents/tab_contents_view_mac.mm
+++ b/chrome/browser/tab_contents/tab_contents_view_mac.mm
@@ -20,6 +20,7 @@
#include "chrome/browser/renderer_host/render_view_host_factory.h"
#include "chrome/browser/renderer_host/render_widget_host.h"
#include "chrome/browser/renderer_host/render_widget_host_view_mac.h"
+#include "chrome/browser/tab_contents/popup_menu_helper_mac.h"
#include "chrome/browser/tab_contents/render_view_context_menu_mac.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_delegate.h"
@@ -252,6 +253,19 @@ void TabContentsViewMac::ShowContextMenu(const ContextMenuParams& params) {
menu.Init();
}
+// Display a popup menu for WebKit using Cocoa widgets.
+void TabContentsViewMac::ShowPopupMenu(
+ const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) {
+ PopupMenuHelper popup_menu_helper(tab_contents()->render_view_host());
+ popup_menu_helper.ShowPopupMenu(bounds, item_height, item_font_size,
+ selected_item, items, right_aligned);
+}
+
RenderWidgetHostView* TabContentsViewMac::CreateNewWidgetInternal(
int route_id,
WebKit::WebPopupType popup_type) {
@@ -263,9 +277,6 @@ RenderWidgetHostView* TabContentsViewMac::CreateNewWidgetInternal(
static_cast<RenderWidgetHostViewMac*>(widget_view);
[widget_view_mac->native_view() retain];
- // |widget_view_mac| needs to know how to position itself in our view.
- widget_view_mac->set_parent_view(cocoa_view_);
-
return widget_view;
}
diff --git a/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc b/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc
index e13d441..aa9fc2c 100644
--- a/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc
+++ b/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc
@@ -393,6 +393,16 @@ void TabContentsViewGtk::ShowContextMenu(const ContextMenuParams& params) {
MessageLoop::current()->SetNestableTasksAllowed(old_state);
}
+void TabContentsViewGtk::ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) {
+ // External popup menus are only used on Mac.
+ NOTREACHED();
+}
+
gboolean TabContentsViewGtk::OnButtonPress(GtkWidget* widget,
GdkEventButton* event) {
last_mouse_down_ = *event;
diff --git a/chrome/browser/views/tab_contents/tab_contents_view_gtk.h b/chrome/browser/views/tab_contents/tab_contents_view_gtk.h
index a46c4a3..890c6b1 100644
--- a/chrome/browser/views/tab_contents/tab_contents_view_gtk.h
+++ b/chrome/browser/views/tab_contents/tab_contents_view_gtk.h
@@ -65,6 +65,12 @@ class TabContentsViewGtk : public TabContentsView,
// Backend implementation of RenderViewHostDelegate::View.
virtual void ShowContextMenu(const ContextMenuParams& params);
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned);
virtual void StartDragging(const WebDropData& drop_data,
WebKit::WebDragOperationsMask ops_allowed,
const SkBitmap& image,
diff --git a/chrome/browser/views/tab_contents/tab_contents_view_win.cc b/chrome/browser/views/tab_contents/tab_contents_view_win.cc
index 91e2036..ce6bf8ed 100644
--- a/chrome/browser/views/tab_contents/tab_contents_view_win.cc
+++ b/chrome/browser/views/tab_contents/tab_contents_view_win.cc
@@ -343,6 +343,16 @@ void TabContentsViewWin::ShowContextMenu(const ContextMenuParams& params) {
MessageLoop::current()->SetNestableTasksAllowed(old_state);
}
+void TabContentsViewWin::ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) {
+ // External popup menus are only used on Mac.
+ NOTREACHED();
+}
+
void TabContentsViewWin::OnHScroll(int scroll_type, short position,
HWND scrollbar) {
ScrollCommon(WM_HSCROLL, scroll_type, position, scrollbar);
diff --git a/chrome/browser/views/tab_contents/tab_contents_view_win.h b/chrome/browser/views/tab_contents/tab_contents_view_win.h
index 471869c..a42ffd7 100644
--- a/chrome/browser/views/tab_contents/tab_contents_view_win.h
+++ b/chrome/browser/views/tab_contents/tab_contents_view_win.h
@@ -59,6 +59,12 @@ class TabContentsViewWin : public TabContentsView,
// Backend implementation of RenderViewHostDelegate::View.
virtual void ShowContextMenu(const ContextMenuParams& params);
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned);
virtual void StartDragging(const WebDropData& drop_data,
WebKit::WebDragOperationsMask operations,
const SkBitmap& image,
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 4aa094a..525a84d 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2833,6 +2833,8 @@
'browser/tab_contents/navigation_entry.cc',
'browser/tab_contents/navigation_entry.h',
'browser/tab_contents/page_navigator.h',
+ 'browser/tab_contents/popup_menu_helper_mac.mm',
+ 'browser/tab_contents/popup_menu_helper_mac.h',
'browser/tab_contents/provisional_load_details.cc',
'browser/tab_contents/provisional_load_details.h',
'browser/tab_contents/render_view_context_menu.cc',
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi
index af9c6fd..9e84f6a 100644
--- a/chrome/chrome_renderer.gypi
+++ b/chrome/chrome_renderer.gypi
@@ -64,6 +64,8 @@
'renderer/extensions/js_only_v8_extensions.h',
'renderer/extensions/renderer_extension_bindings.cc',
'renderer/extensions/renderer_extension_bindings.h',
+ 'renderer/external_popup_menu.cc',
+ 'renderer/external_popup_menu.h',
'renderer/ggl/ggl.cc',
'renderer/ggl/ggl.h',
'renderer/loadtimes_extension_bindings.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 2b6b02b..40151e2 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -2127,6 +2127,9 @@
'dependencies+++': [
'../third_party/WebKit/WebCore/WebCore.gyp/WebCore.gyp:webcore',
],
+ 'sources': [
+ 'renderer/external_popup_menu_unittest.cc',
+ ],
}, { # else: OS != "mac"
'sources!': [
'browser/extensions/browser_action_test_util_mac.mm',
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index c0eac3a..5a4f57f 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -1113,6 +1113,10 @@ IPC_BEGIN_MESSAGES(View)
IPC_MESSAGE_CONTROL1(ViewMsg_SetPhishingModel,
IPC::PlatformFileForTransit /* model_file */)
+ // External popup menus.
+ IPC_MESSAGE_ROUTED1(ViewMsg_SelectPopupMenuItem,
+ int /* selected index, -1 means no selection */)
+
IPC_END_MESSAGES(View)
diff --git a/chrome/renderer/external_popup_menu.cc b/chrome/renderer/external_popup_menu.cc
new file mode 100644
index 0000000..b840386
--- /dev/null
+++ b/chrome/renderer/external_popup_menu.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2010 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/renderer/external_popup_menu.h"
+
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/renderer/render_view.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebExternalPopupMenuClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+
+ExternalPopupMenu::ExternalPopupMenu(
+ RenderView* render_view,
+ const WebKit::WebPopupMenuInfo& popup_menu_info,
+ WebKit::WebExternalPopupMenuClient* popup_menu_client)
+ : render_view_(render_view),
+ popup_menu_info_(popup_menu_info),
+ popup_menu_client_(popup_menu_client) {
+}
+
+void ExternalPopupMenu::show(const WebKit::WebRect& bounds) {
+ ViewHostMsg_ShowPopup_Params popup_params;
+ popup_params.bounds = bounds;
+ popup_params.item_height = popup_menu_info_.itemHeight;
+ popup_params.item_font_size = popup_menu_info_.itemFontSize;
+ popup_params.selected_item = popup_menu_info_.selectedIndex;
+ for (size_t i = 0; i < popup_menu_info_.items.size(); ++i)
+ popup_params.popup_items.push_back(WebMenuItem(popup_menu_info_.items[i]));
+ popup_params.right_aligned = popup_menu_info_.rightAligned;
+ render_view_->Send(
+ new ViewHostMsg_ShowPopup(render_view_->routing_id(), popup_params));
+}
+
+void ExternalPopupMenu::close() {
+ popup_menu_client_ = NULL;
+ render_view_ = NULL;
+}
+
+void ExternalPopupMenu::DidSelectItem(int index) {
+ if (!popup_menu_client_)
+ return;
+ if (index == -1)
+ popup_menu_client_->didCancel();
+ else
+ popup_menu_client_->didAcceptIndex(index);
+}
diff --git a/chrome/renderer/external_popup_menu.h b/chrome/renderer/external_popup_menu.h
new file mode 100644
index 0000000..8fcb635
--- /dev/null
+++ b/chrome/renderer/external_popup_menu.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 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_RENDERER_EXTERNAL_POPUP_MENU_H_
+#define CHROME_RENDERER_EXTERNAL_POPUP_MENU_H_
+
+#include "base/basictypes.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebExternalPopupMenu.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPopupMenuInfo.h"
+
+class RenderView;
+namespace WebKit {
+class WebExternalPopupMenuClient;
+}
+
+class ExternalPopupMenu : public WebKit::WebExternalPopupMenu {
+ public:
+ ExternalPopupMenu(RenderView* render_view,
+ const WebKit::WebPopupMenuInfo& popup_menu_info,
+ WebKit::WebExternalPopupMenuClient* popup_menu_client);
+
+ // Called when the user has selected an item. |selected_item| is -1 if the
+ // user canceled the popup.
+ void DidSelectItem(int selected_index);
+
+ // WebKit::WebExternalPopupMenu implementation:
+ virtual void show(const WebKit::WebRect& bounds);
+ virtual void close();
+
+ private:
+ RenderView* render_view_;
+ WebKit::WebPopupMenuInfo popup_menu_info_;
+ WebKit::WebExternalPopupMenuClient* popup_menu_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalPopupMenu);
+};
+
+#endif // CHROME_RENDERER_EXTERNAL_POPUP_MENU_H_
+
diff --git a/chrome/renderer/external_popup_menu_unittest.cc b/chrome/renderer/external_popup_menu_unittest.cc
new file mode 100644
index 0000000..9ab6067
--- /dev/null
+++ b/chrome/renderer/external_popup_menu_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright (c) 2010 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 "base/utf_string_conversions.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/test/render_view_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSize.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+
+// Tests for the external select popup menu (Mac specific).
+
+namespace {
+
+const char* const kSelectID = "mySelect";
+
+} // namespace
+
+class ExternalPopupMenuTest : public RenderViewTest {
+ public:
+ ExternalPopupMenuTest() {}
+
+ virtual void SetUp() {
+ RenderViewTest::SetUp();
+ // We need to set this explictly as RenderMain is not run.
+ WebKit::WebView::setUseExternalPopupMenus(true);
+
+ // Load the test page.
+ LoadHTML("<select id='mySelect'>"
+ " <option>zero</option>"
+ " <option selected='1'>one</option>"
+ " <option>two</option>"
+ "</select>");
+
+ // Set a minimum size and give focus so simulated events work.
+ view_->webwidget()->resize(WebKit::WebSize(500, 500));
+ view_->webwidget()->setFocus(true);
+ }
+
+ int GetSelectedIndex() {
+ string16 script(ASCIIToUTF16(kSelectID));
+ script.append(ASCIIToUTF16(".selectedIndex"));
+ int selected_index = -1;
+ ExecuteJavaScriptAndReturnIntValue(script, &selected_index);
+ return selected_index;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalPopupMenuTest);
+};
+
+// Normal case: test showing a select popup, canceling/selecting an item.
+TEST_F(ExternalPopupMenuTest, NormalCase) {
+ IPC::TestSink& sink = render_thread_.sink();
+
+ // Click the text field once.
+ EXPECT_TRUE(SimulateElementClick(kSelectID));
+
+ // We should have sent a message to the browser to show the popup menu.
+ const IPC::Message* message =
+ sink.GetUniqueMessageMatching(ViewHostMsg_ShowPopup::ID);
+ ASSERT_TRUE(message != NULL);
+ Tuple1<ViewHostMsg_ShowPopup_Params> param;
+ ViewHostMsg_ShowPopup::Read(message, &param);
+ ASSERT_EQ(3U, param.a.popup_items.size());
+ EXPECT_EQ(1, param.a.selected_item);
+
+ // Simulate the user canceling the popup, the index should not have changed.
+ view_->OnSelectPopupMenuItem(-1);
+ EXPECT_EQ(1, GetSelectedIndex());
+
+ // Show the pop-up again and this time make a selection.
+ EXPECT_TRUE(SimulateElementClick(kSelectID));
+ view_->OnSelectPopupMenuItem(0);
+ EXPECT_EQ(0, GetSelectedIndex());
+
+ // Show the pop-up again and make another selection.
+ sink.ClearMessages();
+ EXPECT_TRUE(SimulateElementClick(kSelectID));
+ message = sink.GetUniqueMessageMatching(ViewHostMsg_ShowPopup::ID);
+ ASSERT_TRUE(message != NULL);
+ ViewHostMsg_ShowPopup::Read(message, &param);
+ ASSERT_EQ(3U, param.a.popup_items.size());
+ EXPECT_EQ(0, param.a.selected_item);
+}
+
+// Page shows popup, then navigates away while popup showing, then select.
+TEST_F(ExternalPopupMenuTest, ShowPopupThenNavigate) {
+ // Click the text field once.
+ EXPECT_TRUE(SimulateElementClick(kSelectID));
+
+ // Now we navigate to another pager.
+ LoadHTML("<blink>Awesome page!</blink>");
+
+ // Now the user selects something, we should not crash.
+ view_->OnSelectPopupMenuItem(-1);
+}
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 2b1615d0..d2f0b15 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -60,6 +60,7 @@
#include "chrome/renderer/extensions/extension_renderer_info.h"
#include "chrome/renderer/extensions/renderer_extension_bindings.h"
#include "chrome/renderer/external_host_bindings.h"
+#include "chrome/renderer/external_popup_menu.h"
#include "chrome/renderer/geolocation_dispatcher_old.h"
#include "chrome/renderer/ggl/ggl.h"
#include "chrome/renderer/localized_error.h"
@@ -200,6 +201,8 @@ using WebKit::WebDragData;
using WebKit::WebDragOperation;
using WebKit::WebDragOperationsMask;
using WebKit::WebEditingAction;
+using WebKit::WebExternalPopupMenu;
+using WebKit::WebExternalPopupMenuClient;
using WebKit::WebFileChooserCompletion;
using WebKit::WebFileSystem;
using WebKit::WebFileSystemCallbacks;
@@ -844,6 +847,9 @@ void RenderView::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(ViewMsg_AccessibilityNotifications_ACK,
OnAccessibilityNotificationsAck)
IPC_MESSAGE_HANDLER(ViewMsg_AsyncOpenFile_ACK, OnAsyncFileOpened)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(ViewMsg_SelectPopupMenuItem, OnSelectPopupMenuItem)
+#endif
IPC_MESSAGE_HANDLER(ViewMsg_PrintPreview, OnPrintPreview)
// Have the super handle all other messages.
@@ -1832,6 +1838,15 @@ WebWidget* RenderView::createPopupMenu(const WebPopupMenuInfo& info) {
return widget->webwidget();
}
+WebExternalPopupMenu* RenderView::createExternalPopupMenu(
+ const WebPopupMenuInfo& popup_menu_info,
+ WebExternalPopupMenuClient* popup_menu_client) {
+ DCHECK(!external_popup_menu_.get());
+ external_popup_menu_.reset(
+ new ExternalPopupMenu(this, popup_menu_info, popup_menu_client));
+ return external_popup_menu_.get();
+}
+
WebWidget* RenderView::createFullscreenWindow(WebKit::WebPopupType popup_type) {
RenderWidget* widget = RenderWidgetFullscreen::Create(routing_id_,
render_thread_,
@@ -6078,3 +6093,10 @@ void RenderView::OnAsyncFileOpened(base::PlatformFileError error_code,
IPC::PlatformFileForTransitToPlatformFile(file_for_transit),
message_id);
}
+
+#if defined(OS_MACOSX)
+void RenderView::OnSelectPopupMenuItem(int selected_index) {
+ external_popup_menu_->DidSelectItem(selected_index);
+ external_popup_menu_.reset();
+}
+#endif
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index 0821aef..a0dcf34 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -28,6 +28,7 @@
#include "chrome/common/render_messages_params.h"
#include "chrome/common/renderer_preferences.h"
#include "chrome/common/view_types.h"
+#include "chrome/renderer/external_popup_menu.h"
#include "chrome/renderer/pepper_plugin_delegate_impl.h"
#include "chrome/renderer/render_widget.h"
#include "chrome/renderer/renderer_webcookiejar_impl.h"
@@ -351,6 +352,9 @@ class RenderView : public RenderWidget,
virtual WebKit::WebWidget* createPopupMenu(WebKit::WebPopupType popup_type);
virtual WebKit::WebWidget* createPopupMenu(
const WebKit::WebPopupMenuInfo& info);
+ virtual WebKit::WebExternalPopupMenu* createExternalPopupMenu(
+ const WebKit::WebPopupMenuInfo& popup_menu_info,
+ WebKit::WebExternalPopupMenuClient* popup_menu_client);
virtual WebKit::WebWidget* createFullscreenWindow(
WebKit::WebPopupType popup_type);
virtual WebKit::WebStorageNamespace* createSessionStorageNamespace(
@@ -630,8 +634,12 @@ class RenderView : public RenderWidget,
private:
// For unit tests.
- friend class RenderViewTest;
+ friend class ExternalPopupMenuTest;
friend class PepperDeviceTest;
+ friend class RenderViewTest;
+
+ FRIEND_TEST_ALL_PREFIXES(ExternalPopupMenuTest, NormalCase);
+ FRIEND_TEST_ALL_PREFIXES(ExternalPopupMenuTest, ShowPopupThenNavigate);
FRIEND_TEST_ALL_PREFIXES(RenderViewTest, OnNavStateChanged);
FRIEND_TEST_ALL_PREFIXES(RenderViewTest, OnImeStateChanged);
FRIEND_TEST_ALL_PREFIXES(RenderViewTest, ImeComposition);
@@ -890,6 +898,7 @@ class RenderView : public RenderWidget,
#if defined(OS_MACOSX)
void OnWindowFrameChanged(const gfx::Rect& window_frame,
const gfx::Rect& view_frame);
+ void OnSelectPopupMenuItem(int selected_index);
#endif
void OnZoom(PageZoom::Function function);
@@ -1376,6 +1385,9 @@ class RenderView : public RenderWidget,
// External host exposed through automation controller.
scoped_ptr<ExternalHostBindings> external_host_bindings_;
+ // The external popup for the currently showing select popup.
+ scoped_ptr<ExternalPopupMenu> external_popup_menu_;
+
// ---------------------------------------------------------------------------
// ADDING NEW DATA? Please see if it fits appropriately in one of the above
// sections rather than throwing it randomly at the end. If you're adding a
diff --git a/chrome/renderer/renderer_main.cc b/chrome/renderer/renderer_main.cc
index 4dd2ed7..29d6a84 100644
--- a/chrome/renderer/renderer_main.cc
+++ b/chrome/renderer/renderer_main.cc
@@ -36,6 +36,7 @@
#if defined(OS_MACOSX)
#include "base/eintr_wrapper.h"
#include "chrome/app/breakpad_mac.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
#endif // OS_MACOSX
#if defined(USE_LINUX_BREAKPAD)
@@ -227,6 +228,9 @@ int RendererMain(const MainFunctionParams& parameters) {
startup_timer(chrome::Counters::renderer_main());
#if defined(OS_MACOSX)
+ // On Mac, the select popups are rendered by the browser.
+ WebKit::WebView::setUseExternalPopupMenus(true);
+
// As long as we use Cocoa in the renderer (for the forseeable future as of
// now; see http://crbug.com/13890 for info) we need to have a UI loop.
MessageLoop main_message_loop(MessageLoop::TYPE_UI);
diff --git a/chrome/test/render_view_test.cc b/chrome/test/render_view_test.cc
index c17c25f..7e6435e 100644
--- a/chrome/test/render_view_test.cc
+++ b/chrome/test/render_view_test.cc
@@ -61,6 +61,20 @@ void RenderViewTest::ExecuteJavaScript(const char* js) {
GetMainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(js)));
}
+bool RenderViewTest::ExecuteJavaScriptAndReturnIntValue(
+ const string16& script,
+ int* int_result) {
+ v8::Handle<v8::Value> result =
+ GetMainFrame()->executeScriptAndReturnValue(WebScriptSource(script));
+ if (result.IsEmpty() || !result->IsInt32())
+ return false;
+
+ if (int_result)
+ *int_result = result->Int32Value();
+
+ return true;
+}
+
void RenderViewTest::LoadHTML(const char* html) {
std::string url_str = "data:text/html;charset=utf-8,";
url_str.append(html);
diff --git a/chrome/test/render_view_test.h b/chrome/test/render_view_test.h
index 1ec224b..bb3e498 100644
--- a/chrome/test/render_view_test.h
+++ b/chrome/test/render_view_test.h
@@ -48,6 +48,12 @@ class RenderViewTest : public testing::Test {
// is a NULL-terminated UTF-8 string.
void ExecuteJavaScript(const char* js);
+ // Executes the given JavaScript and sets the int value it evaluates to in
+ // |result|.
+ // Returns true if the JavaScript was evaluated correctly to an int value,
+ // false otherwise.
+ bool ExecuteJavaScriptAndReturnIntValue(const string16& script, int* result);
+
// Loads the given HTML into the main frame as a data: URL.
void LoadHTML(const char* html);