| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/base/cocoa/menu_utils.h" |
| |
| #include <optional> |
| |
| #import <AppKit/AppKit.h> |
| |
| #import "base/mac/scoped_sending_event.h" |
| #import "base/message_loop/message_pump_apple.h" |
| #include "base/task/current_thread.h" |
| #include "ui/base/interaction/element_tracker_mac.h" |
| #include "ui/gfx/mac/coordinate_conversion.h" |
| |
| namespace ui { |
| |
| NSEvent* EventForPositioningContextMenu(const gfx::Point& anchor, |
| NSWindow* window) { |
| NSPoint location_in_window = |
| [window convertPointFromScreen:gfx::ScreenPointToNSPoint(anchor)]; |
| return EventForPositioningContextMenuRelativeToWindow(location_in_window, |
| window); |
| } |
| |
| NSEvent* EventForPositioningContextMenuRelativeToWindow(const NSPoint& anchor, |
| NSWindow* window) { |
| NSEvent* event = NSApp.currentEvent; |
| switch (event.type) { |
| case NSEventTypeLeftMouseDown: |
| case NSEventTypeLeftMouseUp: |
| case NSEventTypeRightMouseDown: |
| case NSEventTypeRightMouseUp: |
| case NSEventTypeOtherMouseDown: |
| case NSEventTypeOtherMouseUp: |
| return event; |
| default: |
| break; |
| } |
| return [NSEvent mouseEventWithType:NSEventTypeRightMouseDown |
| location:anchor |
| modifierFlags:0 |
| timestamp:0 |
| windowNumber:window.windowNumber |
| context:nil |
| eventNumber:0 |
| clickCount:1 |
| pressure:0]; |
| } |
| |
| void ShowContextMenu(NSMenu* menu, |
| NSEvent* event, |
| NSView* view, |
| bool allow_nested_tasks, |
| ElementContext context) { |
| // Make sure events can be pumped while the menu is up. |
| std::optional< |
| base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop> |
| allow; |
| if (allow_nested_tasks) { |
| allow.emplace(); |
| } |
| |
| // 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. |
| std::optional<base::mac::ScopedSendingEvent> sendingEventScoper; |
| if (allow_nested_tasks) { |
| sendingEventScoper.emplace(); |
| } |
| |
| // Ensure the UI can update while the menu is fading out. |
| base::ScopedPumpMessagesInPrivateModes pump_private; |
| |
| if (context) { |
| ui::ElementTrackerMac::GetInstance()->NotifyMenuWillShow(menu, context); |
| } |
| |
| // Show the menu. |
| [NSMenu popUpContextMenu:menu withEvent:event forView:view]; |
| |
| if (context) { |
| ui::ElementTrackerMac::GetInstance()->NotifyMenuDoneShowing(menu); |
| } |
| } |
| |
| } // namespace ui |