1// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights 2// reserved. Use of this source code is governed by a BSD-style license that can 3// be found in the LICENSE file. 4 5#include "libcef/browser/native/menu_runner_mac.h" 6 7#include "libcef/browser/alloy/alloy_browser_host_impl.h" 8 9#include "base/compiler_specific.h" 10#import "base/mac/scoped_sending_event.h" 11#include "base/task/current_thread.h" 12#import "ui/base/cocoa/menu_controller.h" 13#include "ui/gfx/geometry/point.h" 14 15CefMenuRunnerMac::CefMenuRunnerMac() {} 16 17CefMenuRunnerMac::~CefMenuRunnerMac() {} 18 19bool CefMenuRunnerMac::RunContextMenu( 20 AlloyBrowserHostImpl* browser, 21 CefMenuModelImpl* model, 22 const content::ContextMenuParams& params) { 23 // Create a menu controller based on the model. 24 menu_controller_.reset([[MenuControllerCocoa alloc] 25 initWithModel:model->model() 26 delegate:nil 27 useWithPopUpButtonCell:NO]); 28 29 // Keep the menu controller alive (by adding an additional retain) until after 30 // the menu has been dismissed. Otherwise it will crash if the browser is 31 // destroyed (and consequently the menu controller is destroyed) while the 32 // menu is still pending. 33 base::scoped_nsobject<MenuControllerCocoa> menu_controller_ref( 34 menu_controller_); 35 36 // Make sure events can be pumped while the menu is up. 37 base::CurrentThread::ScopedNestableTaskAllower allow; 38 39 // One of the events that could be pumped is |window.close()|. 40 // User-initiated event-tracking loops protect against this by 41 // setting flags in -[CrApplication sendEvent:], but since 42 // web-content menus are initiated by IPC message the setup has to 43 // be done manually. 44 base::mac::ScopedSendingEvent sendingEventScoper; 45 46 // Show the menu. Blocks until the menu is dismissed. 47 if (browser->IsWindowless()) { 48 // Don't show the menu unless a native window handle exists. 49 if (!browser->GetWindowHandle()) 50 return false; 51 52 const gfx::Point& screen_point = 53 browser->GetScreenPoint(gfx::Point(params.x, params.y)); 54 NSPoint screen_position = NSPointFromCGPoint(screen_point.ToCGPoint()); 55 [[menu_controller_ menu] popUpMenuPositioningItem:nil 56 atLocation:screen_position 57 inView:nil]; 58 } else { 59 NSView* parent_view = 60 browser->web_contents()->GetContentNativeView().GetNativeNSView(); 61 62 // Synthesize an event for the click, as there is no certainty that 63 // [NSApp currentEvent] will return a valid event. 64 NSEvent* currentEvent = [NSApp currentEvent]; 65 NSWindow* window = [parent_view window]; 66 67 NSPoint position = [window mouseLocationOutsideOfEventStream]; 68 69 NSTimeInterval eventTime = [currentEvent timestamp]; 70 NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown 71 location:position 72 modifierFlags:0 73 timestamp:eventTime 74 windowNumber:[window windowNumber] 75 context:nil 76 eventNumber:0 77 clickCount:1 78 pressure:1.0]; 79 80 [NSMenu popUpContextMenu:[menu_controller_ menu] 81 withEvent:clickEvent 82 forView:parent_view]; 83 } 84 85 return true; 86} 87 88void CefMenuRunnerMac::CancelContextMenu() { 89 if (menu_controller_.get()) 90 [menu_controller_ cancel]; 91} 92