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>
Wed, 25 Jan 2017 09:25:52 -0500
changeset 331015 2823fca4f54792fec980a81e6fdf31171a1f9462
parent 331014 39fc2de38a9628e0a3d244f000db17bc0f26af44
child 331016 bb0de976a39eccbdd7ed817de4c1e9379f65c4a7
push id86137
push userspohl@mozilla.com
push dateWed, 25 Jan 2017 14:25:54 +0000
treeherdermozilla-inbound@2823fca4f547 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs429824
milestone54.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
@@ -5469,20 +5469,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