Bug 429824: Let OSX's native menu system handle keyboard shortcuts that we did not handle ourselves via XBL prototype handlers. This allows for handling of custom shortcuts, but does not allow for reassignment of existing shortcuts. r=mstange
authorStephen A Pohl <spohl.mozilla.bugs@gmail.com>
Mon, 15 May 2017 22:59:28 -0400
changeset 358491 92ab3e4bdcb957b1fed5f0495f6d9f95e7b3f3cb
parent 358490 79cfc96ccb7763b5bcc25549555df307cbc02c8f
child 358492 dc96471117ab6243119c8593b5f562e413036f81
push id90344
push userspohl@mozilla.com
push dateTue, 16 May 2017 02:59:56 +0000
treeherdermozilla-inbound@dc96471117ab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs429824
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 429824: Let OSX's native menu system handle keyboard shortcuts that we did not handle ourselves via XBL prototype handlers. This allows for handling of custom shortcuts, but does not allow for reassignment of existing shortcuts. r=mstange
widget/cocoa/nsChildView.mm
widget/cocoa/nsMenuBarX.h
widget/cocoa/nsMenuBarX.mm
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -5549,20 +5549,33 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
 #endif // #if !defined(RELEASE_OR_BETA) || defined(DEBUG)
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
   bool handled = false;
   if (mGeckoChild && mTextInputHandler) {
     handled = mTextInputHandler->HandleKeyDownEvent(theEvent);
   }
 
-  // We always allow keyboard events to propagate to keyDown: but if they are not
-  // handled we give special Application menu items a chance to act.
+  // We always allow keyboard events to propagate to keyDown: but if they are
+  // not handled we give menu items a chance to act. This allows for handling of
+  // custom shortcuts. Note that existing shortcuts cannot be reassigned yet and
+  // will have been handled by keyDown: before we get here.
+  if (!handled && mGeckoChild) {
+    nsCocoaWindow* widget = mGeckoChild->GetXULWindowWidget();
+    if (widget) {
+      nsMenuBarX* mb = widget->GetMenuBar();
+      if (mb) {
+        // Check if main menu wants to handle the event.
+        handled = mb->PerformKeyEquivalent(theEvent);
+      }
+    }
+  }
   if (!handled && sApplicationMenu) {
-    [sApplicationMenu performKeyEquivalent:theEvent];
+    // Check if application menu wants to handle the event.
+    handled = [sApplicationMenu performKeyEquivalent:theEvent];
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)keyUp:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
--- a/widget/cocoa/nsMenuBarX.h
+++ b/widget/cocoa/nsMenuBarX.h
@@ -42,16 +42,17 @@ protected:
   virtual ~nsNativeMenuServiceX() {}
 };
 
 // Objective-C class used to allow us to intervene with keyboard event handling.
 // We allow mouse actions to work normally.
 @interface GeckoNSMenu : NSMenu
 {
 }
+- (BOOL)performSuperKeyEquivalent:(NSEvent*)theEvent;
 @end
 
 // Objective-C class used as action target for menu items
 @interface NativeMenuItemTarget : NSObject
 {
 }
 -(IBAction)menuItemHit:(id)sender;
 @end
@@ -114,16 +115,17 @@ public:
   void              SetSystemHelpMenu();
   nsresult          Paint();
   void              ForceUpdateNativeMenuAt(const nsAString& indexString);
   void              ForceNativeMenuReload(); // used for testing
   static char       GetLocalizedAccelKey(const char *shortcutID);
   static void       ResetNativeApplicationMenu();
   void              SetNeedsRebuild();
   void              ApplicationMenuOpened();
+  bool              PerformKeyEquivalent(NSEvent* theEvent);
 
 protected:
   void              ConstructNativeMenus();
   void              ConstructFallbackNativeMenus();
   nsresult          InsertMenuAtIndex(nsMenuX* aMenu, uint32_t aIndex);
   void              RemoveMenuAtIndex(uint32_t aIndex);
   void              HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode);
   void              AquifyMenuBar();
--- a/widget/cocoa/nsMenuBarX.mm
+++ b/widget/cocoa/nsMenuBarX.mm
@@ -522,16 +522,21 @@ void nsMenuBarX::ApplicationMenuOpened()
     if (!mMenuArray.IsEmpty()) {
       ResetNativeApplicationMenu();
       CreateApplicationMenu(mMenuArray[0].get());
     }
     mNeedsRebuild = false;
   }
 }
 
+bool nsMenuBarX::PerformKeyEquivalent(NSEvent* theEvent)
+{
+  return [mNativeMenu performSuperKeyEquivalent:theEvent];
+}
+
 // Hide the item in the menu by setting the 'hidden' attribute. Returns it in |outHiddenNode| so
 // the caller can hang onto it if they so choose. It is acceptable to pass nsull
 // for |outHiddenNode| if the caller doesn't care about the hidden node.
 void nsMenuBarX::HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode)
 {
   nsCOMPtr<nsIDOMElement> menuItem;
   inDoc->GetElementById(inID, getter_AddRefs(menuItem));
   nsCOMPtr<nsIContent> menuContent(do_QueryInterface(menuItem));
@@ -871,16 +876,21 @@ static BOOL gMenuItemsExecuteCommands = 
   if (![NSApp keyWindow] || [[NSApp keyWindow] firstResponder] != firstResponder) {
     return YES;
   }
 
   // Return NO so that we can handle the event via NSView's "keyDown:".
   return NO;
 }
 
+- (BOOL)performSuperKeyEquivalent:(NSEvent*)theEvent
+{
+  return [super performKeyEquivalent:theEvent];
+}
+
 @end
 
 //
 // Objective-C class used as action target for menu items
 //
 
 @implementation NativeMenuItemTarget