diff options
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, ¶m); + 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, ¶m); + 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); |