Fix cmd-tilde for various keyboard layouts by trusting event handled status for command key events. b=417466 r=masayuki sr=roc
authorjoshmoz@gmail.com
Thu, 13 Mar 2008 18:08:04 -0700
changeset 13042 9833c895105893300eb87cf64e9acb3c4f78005c
parent 13041 ce720feb26ddf59156fef762fa4fa12060aed2e2
child 13043 afbf15913832e152c4c17babebad496245bd8cdb
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki, roc
bugs417466
milestone1.9b5pre
Fix cmd-tilde for various keyboard layouts by trusting event handled status for command key events. b=417466 r=masayuki sr=roc
widget/src/cocoa/nsChildView.h
widget/src/cocoa/nsChildView.mm
--- a/widget/src/cocoa/nsChildView.h
+++ b/widget/src/cocoa/nsChildView.h
@@ -59,17 +59,16 @@
 #include "nsIMouseListener.h"
 #include "nsIEventListener.h"
 #include "nsString.h"
 #include "nsIDragService.h"
 #include "nsIMenuBar.h"
 
 #include "nsplugindefs.h"
 
-#undef DARWIN
 #import <Carbon/Carbon.h>
 #import <Cocoa/Cocoa.h>
 
 class gfxASurface;
 class nsChildView;
 union nsPluginPort;
 
 @interface ChildView : NSView<
@@ -93,21 +92,22 @@ union nsPluginPort;
 
   // The following variables are only valid during key down event processing.
   // Their current usage needs to be fixed to avoid problems with nested event
   // loops that can confuse them. Once a variable is set during key down event
   // processing, if an event spawns a nested event loop the previously set value
   // will be wiped out.
   NSEvent* mCurKeyEvent;
   PRBool mKeyDownHandled;
-  BOOL mIgnoreDoCommand;
   // While we process key down events we need to keep track of whether or not
   // we sent a key press event. This helps us make sure we do send one
   // eventually.
   BOOL mKeyPressSent;
+  // Valid when mKeyPressSent is true.
+  PRBool mKeyPressHandled;
 
   // needed for NSTextInput implementation
   NSRange mMarkedRange;
 
   BOOL mInHandScroll; // true for as long as we are hand scrolling
   // hand scroll locations
   NSPoint mHandScrollStartMouseLoc;
   nscoord mHandScrollStartScrollX, mHandScrollStartScrollY;
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -1996,17 +1996,17 @@ NSEvent* gLastDragEvent = nil;
 
   if ((self = [super initWithFrame:inFrame])) {
     mWindow = nil;
     mGeckoChild = inChild;
     mIsPluginView = NO;
 
     mCurKeyEvent = nil;
     mKeyDownHandled = PR_FALSE;
-    mIgnoreDoCommand = NO;
+    mKeyPressHandled = NO;
     mKeyPressSent = NO;
 
     // initialization for NSTextInput
     mMarkedRange.location = NSNotFound;
     mMarkedRange.length = 0;
 
     mLastMenuForEventEvent = nil;
     mDragService = nsnull;
@@ -3995,17 +3995,17 @@ static PRBool IsSpecialGeckoKey(UInt32 m
     } else {
       // Note that insertText is not called only at key pressing.
       if (!IsPrintableChar(geckoEvent.charCode)) {
         geckoEvent.keyCode = GetGeckoKeyCodeFromChar(geckoEvent.charCode);
         geckoEvent.charCode = 0;
       }
     }
 
-    mGeckoChild->DispatchWindowEvent(geckoEvent);
+    mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
     mKeyPressSent = YES;
   }
   else {
     if (!nsTSMManager::IsComposing()) {
       [self sendCompositionEvent:NS_COMPOSITION_START];
       // Note: mGeckoChild might have become null here. Don't count on it from here on.
       nsTSMManager::StartComposing(self);
       // Note: mGeckoChild might have become null here. Don't count on it from here on.
@@ -4045,20 +4045,20 @@ static PRBool IsSpecialGeckoKey(UInt32 m
 }
 
 
 - (void) doCommandBySelector:(SEL)aSelector
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
 #if DEBUG_IME 
-  NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mIgnoreDoCommand);
+  NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mKeyPressHandled);
 #endif
 
-  if (!mIgnoreDoCommand)
+  if (!mKeyPressHandled)
     [super doCommandBySelector:aSelector];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 
 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
 {
@@ -4348,22 +4348,23 @@ static PRBool IsSpecialGeckoKey(UInt32 m
                                       isARepeat:[theEvent isARepeat]
                                         keyCode:[theEvent keyCode]];
   return newEvent;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 
-- (void)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+// Returns PR_TRUE if Gecko claims to have handled the event, PR_FALSE otherwise.
+- (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   if (!mGeckoChild)
-    return;
+    return NO;
 
   nsAutoRetainView kungFuDeathGrip(self);
   mCurKeyEvent = theEvent;
 
   BOOL nonDeadKeyPress = [[theEvent characters] length] > 0;
   if (nonDeadKeyPress) {
     if (![theEvent isARepeat]) {
       NSResponder* firstResponder = [[self window] firstResponder];
@@ -4373,38 +4374,40 @@ static PRBool IsSpecialGeckoKey(UInt32 m
 
       // create native EventRecord for use by plugins
       EventRecord macEvent;
       ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
       geckoEvent.nativeMsg = &macEvent;
 
       mKeyDownHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
       if (!mGeckoChild)
-        return;
+        return mKeyDownHandled;
 
       // The key down event may have shifted the focus, in which
       // case we should not fire the key press.
       if (firstResponder != [[self window] firstResponder]) {
+        PRBool handled = mKeyDownHandled;
         mCurKeyEvent = nil;
         mKeyDownHandled = PR_FALSE;
-        return;
+        return handled;
       }
     }
 
     // If this is the context menu key command, send a context menu key event.
     unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
     if (modifierFlags == NSControlKeyMask && [[theEvent charactersIgnoringModifiers] isEqualToString:@" "]) {
       nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, [self widget], nsMouseEvent::eReal, nsMouseEvent::eContextMenuKey);
       contextMenuEvent.isShift = contextMenuEvent.isControl = contextMenuEvent.isAlt = contextMenuEvent.isMeta = PR_FALSE;
-      mGeckoChild->DispatchWindowEvent(contextMenuEvent);
+      PRBool cmEventHandled = mGeckoChild->DispatchWindowEvent(contextMenuEvent);
       [self maybeInitContextMenuTracking];
       // Bail, there is nothing else to do here.
+      PRBool handled = (cmEventHandled || mKeyDownHandled);
       mCurKeyEvent = nil;
       mKeyDownHandled = PR_FALSE;
-      return;
+      return handled;
     }
 
     nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
     [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
 
     // if this is a non-letter keypress, or the control key is down,
     // dispatch the keydown to gecko, so that we trap delete,
     // control-letter combinations etc before Cocoa tries to use
@@ -4414,56 +4417,60 @@ static PRBool IsSpecialGeckoKey(UInt32 m
       if (mKeyDownHandled)
         geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
 
       // create native EventRecord for use by plugins
       EventRecord macEvent;
       ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
       geckoEvent.nativeMsg = &macEvent;
 
-      mIgnoreDoCommand = mGeckoChild->DispatchWindowEvent(geckoEvent);
+      mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
+      mKeyPressSent = YES;
       if (!mGeckoChild)
-        return;
-      mKeyPressSent = YES;
+        return (mKeyDownHandled || mKeyPressHandled);
     }
   }
 
   // Let Cocoa interpret the key events, caching IsComposing first.
   // We don't do it if this came from performKeyEquivalent because
   // interpretKeyEvents isn't set up to handle those key combinations.
   PRBool wasComposing = nsTSMManager::IsComposing();
   if (!isKeyEquiv && nsTSMManager::IsIMEEnabled())
     [super interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
 
   if (!mGeckoChild)
-    return;
+    return (mKeyDownHandled || mKeyPressHandled);;
 
   if (!mKeyPressSent && nonDeadKeyPress && !wasComposing && !nsTSMManager::IsComposing()) {
     nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
     [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
     if (mKeyDownHandled)
       geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
 
     // create native EventRecord for use by plugins
     EventRecord macEvent;
     ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
     geckoEvent.nativeMsg = &macEvent;
 
-    mGeckoChild->DispatchWindowEvent(geckoEvent);    
+    mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
   }
 
   // Note: mGeckoChild might have become null here. Don't count on it from here on.
 
+  PRBool handled = (mKeyDownHandled || mKeyPressHandled);
+
   // See note about nested event loops where these variables are declared in header.
-  mIgnoreDoCommand = NO;
+  mKeyPressHandled = NO;
   mKeyPressSent = NO;
   mCurKeyEvent = nil;
   mKeyDownHandled = PR_FALSE;
 
-  NS_OBJC_END_TRY_ABORT_BLOCK;
+  return handled;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
 }
 
 
 - (void)keyDown:(NSEvent*)theEvent
 {
   [self processKeyDownEvent:theEvent keyEquiv:NO];
 }
 
@@ -4546,38 +4553,32 @@ static BOOL keyUpAlreadySentKeyDown = NO
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 
 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
-  // First we need to ignore certain system commands. If we don't we'll have
-  // to duplicate their functionality in Gecko, which as of this time we haven't.
-  // The only thing we ignore now is command-tilde, because NSApp handles that for us
-  // and we need the event to propagate to there.
-  unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
-  if (modifierFlags & NSCommandKeyMask && [theEvent keyCode] == kTildeKeyCode)
-    return NO;
-
   // don't do anything if we don't have a gecko widget
   if (!mGeckoChild)
     return NO;
 
   nsAutoRetainView kungFuDeathGrip(self);
 
   // if we aren't the first responder, pass the event on
   if ([[self window] firstResponder] != self)
     return [super performKeyEquivalent:theEvent];
 
   // don't process if we're composing, but don't consume the event
   if (nsTSMManager::IsComposing())
     return NO;
 
+  unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
+
   // see if the menu system will handle the event
   if ([[NSApp mainMenu] performKeyEquivalent:theEvent]) {
     return YES;
   }
   else {
     // On Mac OS X 10.5 NSMenu's performKeyEquivalent: method returns NO for disabled menu
     // items that have a matching key equiv. We need to know if that was the case so we can
     // stop here like we would on 10.4 (it returns YES in that case). Since we want to eat
@@ -4599,18 +4600,23 @@ static BOOL keyUpAlreadySentKeyDown = NO
   }
 
   // don't handle this if certain modifiers are down - those should
   // be sent as normal key up/down events and cocoa will do so automatically
   // if we reject here
   if ((modifierFlags & NSFunctionKeyMask) || (modifierFlags & NSNumericPadKeyMask))
     return NO;
 
-  if ([theEvent type] == NSKeyDown)
-    [self processKeyDownEvent:theEvent keyEquiv:YES];
+  if ([theEvent type] == NSKeyDown) {
+    // We trust the Gecko handled status for cmd key events. See bug 417466 for more info.
+    if (modifierFlags & NSCommandKeyMask)
+      return [self processKeyDownEvent:theEvent keyEquiv:YES];
+    else
+      [self processKeyDownEvent:theEvent keyEquiv:YES];
+  }
 
   return YES;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
 }
 
 
 - (void)flagsChanged:(NSEvent*)theEvent