• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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