1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #import <Carbon/Carbon.h> 6 7 #include "chrome/browser/tab_contents/popup_menu_helper_mac.h" 8 9 #include "base/memory/scoped_nsobject.h" 10 #include "base/message_loop.h" 11 #include "chrome/browser/renderer_host/render_widget_host_view_mac.h" 12 #import "chrome/browser/ui/cocoa/base_view.h" 13 #include "content/browser/renderer_host/render_view_host.h" 14 #import "content/common/chrome_application_mac.h" 15 #include "content/common/notification_source.h" 16 #include "webkit/glue/webmenurunner_mac.h" 17 18 PopupMenuHelper::PopupMenuHelper(RenderViewHost* render_view_host) 19 : render_view_host_(render_view_host) { 20 notification_registrar_.Add( 21 this, NotificationType::RENDER_WIDGET_HOST_DESTROYED, 22 Source<RenderWidgetHost>(render_view_host)); 23 } 24 25 void PopupMenuHelper::ShowPopupMenu( 26 const gfx::Rect& bounds, 27 int item_height, 28 double item_font_size, 29 int selected_item, 30 const std::vector<WebMenuItem>& items, 31 bool right_aligned) { 32 // Retain the Cocoa view for the duration of the pop-up so that it can't be 33 // dealloced if my Destroy() method is called while the pop-up's up (which 34 // would in turn delete me, causing a crash once the -runMenuInView 35 // call returns. That's what was happening in <http://crbug.com/33250>). 36 RenderWidgetHostViewMac* rwhvm = 37 static_cast<RenderWidgetHostViewMac*>(render_view_host_->view()); 38 scoped_nsobject<RenderWidgetHostViewCocoa> cocoa_view 39 ([rwhvm->native_view() retain]); 40 41 // Display the menu. 42 scoped_nsobject<WebMenuRunner> menu_runner; 43 menu_runner.reset([[WebMenuRunner alloc] initWithItems:items 44 fontSize:item_font_size 45 rightAligned:right_aligned]); 46 47 { 48 // Make sure events can be pumped while the menu is up. 49 MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); 50 51 // One of the events that could be pumped is |window.close()|. 52 // User-initiated event-tracking loops protect against this by 53 // setting flags in -[CrApplication sendEvent:], but since 54 // web-content menus are initiated by IPC message the setup has to 55 // be done manually. 56 chrome_application_mac::ScopedSendingEvent sendingEventScoper; 57 58 // Now run a SYNCHRONOUS NESTED EVENT LOOP until the pop-up is finished. 59 [menu_runner runMenuInView:cocoa_view 60 withBounds:[cocoa_view flipRectToNSRect:bounds] 61 initialIndex:selected_item]; 62 } 63 64 if (!render_view_host_) { 65 // Bad news, the RenderViewHost got deleted while we were off running the 66 // menu. Nothing to do. 67 return; 68 } 69 70 if ([menu_runner menuItemWasChosen]) { 71 render_view_host_->DidSelectPopupMenuItem( 72 [menu_runner indexOfSelectedItem]); 73 } else { 74 render_view_host_->DidCancelPopupMenu(); 75 } 76 } 77 78 void PopupMenuHelper::Observe( 79 NotificationType type, 80 const NotificationSource& source, 81 const NotificationDetails& details) { 82 DCHECK(type == NotificationType::RENDER_WIDGET_HOST_DESTROYED); 83 RenderViewHost* rvh = Source<RenderViewHost>(source).ptr(); 84 DCHECK_EQ(render_view_host_, rvh); 85 render_view_host_ = NULL; 86 } 87 88