| 
									
										
										
										
											2014-10-31 11:17:05 -07:00
										 |  |  | // Copyright (c) 2013 GitHub, Inc. | 
					
						
							| 
									
										
										
										
											2014-04-25 17:49:37 +08:00
										 |  |  | // Use of this source code is governed by the MIT license that can be | 
					
						
							| 
									
										
										
										
											2013-05-06 20:27:09 +08:00
										 |  |  | // found in the LICENSE file. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 13:46:59 -07:00
										 |  |  | #import "shell/browser/api/atom_api_menu_mac.h" | 
					
						
							| 
									
										
										
										
											2013-05-06 20:27:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-02 14:05:37 +02:00
										 |  |  | #include <string> | 
					
						
							|  |  |  | #include <utility> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 11:44:25 -08:00
										 |  |  | #include "base/mac/scoped_sending_event.h" | 
					
						
							| 
									
										
										
										
											2019-08-23 21:14:23 -04:00
										 |  |  | #include "base/message_loop/message_loop_current.h" | 
					
						
							| 
									
										
										
										
											2013-05-16 17:25:02 +08:00
										 |  |  | #include "base/strings/sys_string_conversions.h" | 
					
						
							| 
									
										
										
										
											2019-01-12 06:30:43 +05:30
										 |  |  | #include "base/task/post_task.h" | 
					
						
							|  |  |  | #include "content/public/browser/browser_task_traits.h" | 
					
						
							| 
									
										
										
										
											2017-02-15 11:44:25 -08:00
										 |  |  | #include "content/public/browser/browser_thread.h" | 
					
						
							| 
									
										
										
										
											2013-05-14 16:50:56 +08:00
										 |  |  | #include "content/public/browser/web_contents.h" | 
					
						
							| 
									
										
										
										
											2019-06-19 13:46:59 -07:00
										 |  |  | #include "shell/browser/native_window.h" | 
					
						
							|  |  |  | #include "shell/browser/unresponsive_suppressor.h" | 
					
						
							|  |  |  | #include "shell/common/node_includes.h" | 
					
						
							| 
									
										
										
										
											2013-05-14 16:50:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 15:57:36 -08:00
										 |  |  | using content::BrowserThread; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-16 18:19:28 -07:00
										 |  |  | namespace { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static scoped_nsobject<NSMenu> applicationMenu_; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | namespace electron { | 
					
						
							| 
									
										
										
										
											2013-05-06 20:27:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace api { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-15 10:15:23 +09:00
										 |  |  | MenuMac::MenuMac(gin::Arguments* args) : Menu(args), weak_factory_(this) {} | 
					
						
							| 
									
										
										
										
											2013-05-06 20:27:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 16:37:22 -07:00
										 |  |  | MenuMac::~MenuMac() = default; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 17:14:49 +09:00
										 |  |  | void MenuMac::PopupAt(TopLevelWindow* window, | 
					
						
							| 
									
										
										
										
											2018-04-18 15:39:45 +05:30
										 |  |  |                       int x, | 
					
						
							|  |  |  |                       int y, | 
					
						
							|  |  |  |                       int positioning_item, | 
					
						
							| 
									
										
										
										
											2019-11-20 20:17:39 +09:00
										 |  |  |                       base::OnceClosure callback) { | 
					
						
							| 
									
										
										
										
											2014-11-25 16:47:41 +01:00
										 |  |  |   NativeWindow* native_window = window->window(); | 
					
						
							| 
									
										
										
										
											2014-11-28 15:59:03 +08:00
										 |  |  |   if (!native_window) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2017-02-15 11:44:25 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 20:17:39 +09:00
										 |  |  |   // Make sure the Menu object would not be garbage-collected until the callback | 
					
						
							|  |  |  |   // has run. | 
					
						
							|  |  |  |   base::OnceClosure callback_with_ref = BindSelfToClosure(std::move(callback)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-27 18:03:06 -07:00
										 |  |  |   auto popup = | 
					
						
							|  |  |  |       base::BindOnce(&MenuMac::PopupOnUI, weak_factory_.GetWeakPtr(), | 
					
						
							|  |  |  |                      native_window->GetWeakPtr(), window->weak_map_id(), x, y, | 
					
						
							| 
									
										
										
										
											2019-11-20 20:17:39 +09:00
										 |  |  |                      positioning_item, std::move(callback_with_ref)); | 
					
						
							| 
									
										
										
										
											2019-09-05 13:37:09 -07:00
										 |  |  |   base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(popup)); | 
					
						
							| 
									
										
										
										
											2017-02-15 11:44:25 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window, | 
					
						
							| 
									
										
										
										
											2017-09-14 00:13:45 +03:00
										 |  |  |                         int32_t window_id, | 
					
						
							|  |  |  |                         int x, | 
					
						
							|  |  |  |                         int y, | 
					
						
							| 
									
										
										
										
											2018-01-01 16:26:19 +09:00
										 |  |  |                         int positioning_item, | 
					
						
							| 
									
										
										
										
											2019-11-20 20:17:39 +09:00
										 |  |  |                         base::OnceClosure callback) { | 
					
						
							| 
									
										
										
										
											2017-02-15 11:44:25 -08:00
										 |  |  |   if (!native_window) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2019-01-09 12:25:19 -08:00
										 |  |  |   NSWindow* nswindow = native_window->GetNativeWindow().GetNativeNSWindow(); | 
					
						
							| 
									
										
										
										
											2014-11-28 15:59:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 20:17:39 +09:00
										 |  |  |   base::OnceClosure close_callback = | 
					
						
							|  |  |  |       base::BindOnce(&MenuMac::OnClosed, weak_factory_.GetWeakPtr(), window_id, | 
					
						
							|  |  |  |                      std::move(callback)); | 
					
						
							| 
									
										
										
										
											2019-05-01 21:27:55 +02:00
										 |  |  |   popup_controllers_[window_id] = base::scoped_nsobject<AtomMenuController>( | 
					
						
							|  |  |  |       [[AtomMenuController alloc] initWithModel:model() | 
					
						
							|  |  |  |                           useDefaultAccelerator:NO]); | 
					
						
							| 
									
										
										
										
											2017-02-16 10:58:02 -08:00
										 |  |  |   NSMenu* menu = [popup_controllers_[window_id] menu]; | 
					
						
							| 
									
										
										
										
											2018-03-06 16:12:10 +09:00
										 |  |  |   NSView* view = [nswindow contentView]; | 
					
						
							| 
									
										
										
										
											2014-11-25 16:47:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-22 11:17:12 -07:00
										 |  |  |   // Which menu item to show. | 
					
						
							|  |  |  |   NSMenuItem* item = nil; | 
					
						
							|  |  |  |   if (positioning_item < [menu numberOfItems] && positioning_item >= 0) | 
					
						
							|  |  |  |     item = [menu itemAtIndex:positioning_item]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // (-1, -1) means showing on mouse location. | 
					
						
							|  |  |  |   NSPoint position; | 
					
						
							|  |  |  |   if (x == -1 || y == -1) { | 
					
						
							|  |  |  |     position = [view convertPoint:[nswindow mouseLocationOutsideOfEventStream] | 
					
						
							|  |  |  |                          fromView:nil]; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     position = NSMakePoint(x, [view frame].size.height - y); | 
					
						
							| 
									
										
										
										
											2016-01-22 03:18:04 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-01-22 11:17:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-13 09:06:52 -04:00
										 |  |  |   // If no preferred item is specified, try to show all of the menu items. | 
					
						
							|  |  |  |   if (!positioning_item) { | 
					
						
							| 
									
										
										
										
											2016-05-23 21:53:50 +09:00
										 |  |  |     CGFloat windowBottom = CGRectGetMinY([view window].frame); | 
					
						
							| 
									
										
										
										
											2016-05-26 17:43:38 -04:00
										 |  |  |     CGFloat lowestMenuPoint = windowBottom + position.y - [menu size].height; | 
					
						
							|  |  |  |     CGFloat screenBottom = CGRectGetMinY([view window].screen.frame); | 
					
						
							|  |  |  |     CGFloat distanceFromBottom = lowestMenuPoint - screenBottom; | 
					
						
							|  |  |  |     if (distanceFromBottom < 0) | 
					
						
							|  |  |  |       position.y = position.y - distanceFromBottom + 4; | 
					
						
							| 
									
										
										
										
											2016-05-13 09:06:52 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Place the menu left of cursor if it is overflowing off right of screen. | 
					
						
							| 
									
										
										
										
											2016-05-23 21:53:50 +09:00
										 |  |  |   CGFloat windowLeft = CGRectGetMinX([view window].frame); | 
					
						
							| 
									
										
										
										
											2016-05-26 17:43:38 -04:00
										 |  |  |   CGFloat rightmostMenuPoint = windowLeft + position.x + [menu size].width; | 
					
						
							|  |  |  |   CGFloat screenRight = CGRectGetMaxX([view window].screen.frame); | 
					
						
							|  |  |  |   if (rightmostMenuPoint > screenRight) | 
					
						
							| 
									
										
										
										
											2016-05-13 09:06:52 -04:00
										 |  |  |     position.x = position.x - [menu size].width; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 20:17:39 +09:00
										 |  |  |   [popup_controllers_[window_id] setCloseCallback:std::move(close_callback)]; | 
					
						
							| 
									
										
										
										
											2017-09-14 00:13:45 +03:00
										 |  |  |   // Make sure events can be pumped while the menu is up. | 
					
						
							| 
									
										
										
										
											2019-01-09 12:10:26 -08:00
										 |  |  |   base::MessageLoopCurrent::ScopedNestableTaskAllower allow; | 
					
						
							| 
									
										
										
										
											2017-09-14 00:13:45 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // 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. | 
					
						
							|  |  |  |   base::mac::ScopedSendingEvent sendingEventScoper; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Don't emit unresponsive event when showing menu. | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  |   electron::UnresponsiveSuppressor suppressor; | 
					
						
							| 
									
										
										
										
											2017-09-14 00:13:45 +03:00
										 |  |  |   [menu popUpMenuPositioningItem:item atLocation:position inView:view]; | 
					
						
							| 
									
										
										
										
											2014-11-25 16:47:41 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-16 10:58:02 -08:00
										 |  |  | void MenuMac::ClosePopupAt(int32_t window_id) { | 
					
						
							| 
									
										
										
										
											2019-09-05 13:37:09 -07:00
										 |  |  |   auto close_popup = base::BindOnce(&MenuMac::ClosePopupOnUI, | 
					
						
							|  |  |  |                                     weak_factory_.GetWeakPtr(), window_id); | 
					
						
							|  |  |  |   base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, | 
					
						
							|  |  |  |                                                    std::move(close_popup)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void MenuMac::ClosePopupOnUI(int32_t window_id) { | 
					
						
							| 
									
										
										
										
											2018-01-01 15:39:43 +09:00
										 |  |  |   auto controller = popup_controllers_.find(window_id); | 
					
						
							|  |  |  |   if (controller != popup_controllers_.end()) { | 
					
						
							|  |  |  |     // Close the controller for the window. | 
					
						
							|  |  |  |     [controller->second cancel]; | 
					
						
							| 
									
										
										
										
											2017-12-21 15:57:27 +09:00
										 |  |  |   } else if (window_id == -1) { | 
					
						
							| 
									
										
										
										
											2018-01-01 15:39:43 +09:00
										 |  |  |     // Or just close all opened controllers. | 
					
						
							|  |  |  |     for (auto it = popup_controllers_.begin(); | 
					
						
							|  |  |  |          it != popup_controllers_.end();) { | 
					
						
							|  |  |  |       // The iterator is invalidated after the call. | 
					
						
							|  |  |  |       [(it++)->second cancel]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-12-21 15:57:27 +09:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-02-16 10:58:02 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 20:17:39 +09:00
										 |  |  | void MenuMac::OnClosed(int32_t window_id, base::OnceClosure callback) { | 
					
						
							| 
									
										
										
										
											2018-01-01 15:39:43 +09:00
										 |  |  |   popup_controllers_.erase(window_id); | 
					
						
							| 
									
										
										
										
											2019-11-20 20:17:39 +09:00
										 |  |  |   std::move(callback).Run(); | 
					
						
							| 
									
										
										
										
											2018-01-01 15:39:43 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-16 17:25:02 +08:00
										 |  |  | // static | 
					
						
							| 
									
										
										
										
											2014-04-21 23:40:10 +08:00
										 |  |  | void Menu::SetApplicationMenu(Menu* base_menu) { | 
					
						
							|  |  |  |   MenuMac* menu = static_cast<MenuMac*>(base_menu); | 
					
						
							| 
									
										
										
										
											2019-05-01 21:27:55 +02:00
										 |  |  |   base::scoped_nsobject<AtomMenuController> menu_controller( | 
					
						
							|  |  |  |       [[AtomMenuController alloc] initWithModel:menu->model_.get() | 
					
						
							|  |  |  |                           useDefaultAccelerator:YES]); | 
					
						
							| 
									
										
										
										
											2018-05-16 18:19:28 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop]; | 
					
						
							|  |  |  |   [currentRunLoop cancelPerformSelector:@selector(setMainMenu:) | 
					
						
							|  |  |  |                                  target:NSApp | 
					
						
							|  |  |  |                                argument:applicationMenu_]; | 
					
						
							|  |  |  |   applicationMenu_.reset([[menu_controller menu] retain]); | 
					
						
							|  |  |  |   [[NSRunLoop currentRunLoop] | 
					
						
							|  |  |  |       performSelector:@selector(setMainMenu:) | 
					
						
							|  |  |  |                target:NSApp | 
					
						
							|  |  |  |              argument:applicationMenu_ | 
					
						
							|  |  |  |                 order:0 | 
					
						
							|  |  |  |                 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]; | 
					
						
							| 
									
										
										
										
											2013-05-16 10:54:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Ensure the menu_controller_ is destroyed after main menu is set. | 
					
						
							|  |  |  |   menu_controller.swap(menu->menu_controller_); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-16 17:25:02 +08:00
										 |  |  | // static | 
					
						
							| 
									
										
										
										
											2014-04-21 23:40:10 +08:00
										 |  |  | void Menu::SendActionToFirstResponder(const std::string& action) { | 
					
						
							|  |  |  |   SEL selector = NSSelectorFromString(base::SysUTF8ToNSString(action)); | 
					
						
							|  |  |  |   [NSApp sendAction:selector to:nil from:[NSApp mainMenu]]; | 
					
						
							| 
									
										
										
										
											2013-05-16 17:25:02 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-06 20:27:09 +08:00
										 |  |  | // static | 
					
						
							| 
									
										
										
										
											2019-10-15 10:15:23 +09:00
										 |  |  | mate::WrappableBase* Menu::New(gin::Arguments* args) { | 
					
						
							|  |  |  |   return new MenuMac(args); | 
					
						
							| 
									
										
										
										
											2013-05-06 20:27:09 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace api | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | }  // namespace electron |