Bug 494267: Cmd+A selects all text on page even when a plugin has keyboard focus. r=josh
authorSteven Michaud <smichaud@pobox.com>
Mon, 21 Sep 2009 16:48:55 -0500
changeset 32925 deb6e0b6f4f09ac6a3e6087231f5c66001fef131
parent 32924 3a6c1c8137f5af1e5583aad3a175eaf136302727
child 32926 fb31c1bdf4ddc3d2b60e76ee731eb1b91953c608
push idunknown
push userunknown
push dateunknown
reviewersjosh
bugs494267
milestone1.9.3a1pre
Bug 494267: Cmd+A selects all text on page even when a plugin has keyboard focus. r=josh
widget/src/cocoa/nsChildView.mm
widget/src/cocoa/nsMenuBarX.h
widget/src/cocoa/nsMenuBarX.mm
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -5807,47 +5807,65 @@ static BOOL keyUpAlreadySentKeyDown = NO
     if ([firstResponder isKindOfClass:[NSView class]])
       return [super performKeyEquivalent:theEvent];
   }
 
   // don't process if we're composing, but don't consume the event
   if (nsTSMManager::IsComposing())
     return NO;
 
+  UInt32 modifierFlags =
+    nsCocoaUtils::GetCocoaEventModifierFlags(theEvent) & NSDeviceIndependentModifierFlagsMask;
+
   // Set to true if embedding menus handled the event when a plugin has focus.
   // We give menus a crack at handling commands before Gecko in the plugin case.
   BOOL handledByEmbedding = NO;
 
   // Perform native menu UI feedback even if we stop the event from propagating to it normally.
   // Recall that the menu system won't actually execute any commands for keyboard command invocations.
   //
   // If this is a plugin, we do actually perform the action on keyboard commands. See bug 428047.
   // If the action on plugins here changes the first responder, don't continue.
   NSMenu* mainMenu = [NSApp mainMenu];
   if (mIsPluginView) {
     if ([mainMenu isKindOfClass:[GeckoNSMenu class]]) {
-      [(GeckoNSMenu*)mainMenu actOnKeyEquivalent:theEvent];
+      // Maintain a list of cmd+key combinations that we never act on (in the
+      // browser) when the keyboard focus is in a plugin.  What a particular
+      // cmd+key combo means here (to the browser) is governed by browser.dtd,
+      // which "contains the browser main menu items".
+      PRBool dontActOnKeyEquivalent = PR_FALSE;
+      if (modifierFlags == NSCommandKeyMask) {
+        NSString *unmodchars = [theEvent charactersIgnoringModifiers];
+        if ([unmodchars length] == 1) {
+          if ([unmodchars characterAtIndex:0] ==
+              nsMenuBarX::GetLocalizedAccelKey("key_selectAll"))
+            dontActOnKeyEquivalent = PR_TRUE;
+        }
+      }
+      if (dontActOnKeyEquivalent) {
+        [(GeckoNSMenu*)mainMenu performMenuUserInterfaceEffectsForEvent:theEvent];
+      } else {
+        [(GeckoNSMenu*)mainMenu actOnKeyEquivalent:theEvent];
+      }
     }
     else {
       // This is probably an embedding situation. If the native menu handle the event
       // then return YES from pKE no matter what Gecko or the plugin does.
       handledByEmbedding = [mainMenu performKeyEquivalent:theEvent];
     }
     if ([[self window] firstResponder] != self)
       return YES;
   }
   else {
     if ([mainMenu isKindOfClass:[GeckoNSMenu class]])
       [(GeckoNSMenu*)mainMenu performMenuUserInterfaceEffectsForEvent:theEvent];
   }
 
   // With Cmd key or Ctrl+Tab or Ctrl+Esc, keyDown will be never called.
   // Therefore, we need to call processKeyDownEvent from performKeyEquivalent.
-  UInt32 modifierFlags =
-    nsCocoaUtils::GetCocoaEventModifierFlags(theEvent) & NSDeviceIndependentModifierFlagsMask;
   UInt32 keyCode = nsCocoaUtils::GetCocoaEventKeyCode(theEvent);
   PRBool keyDownNeverFiredEvent = (modifierFlags & NSCommandKeyMask) ||
            ((modifierFlags & NSControlKeyMask) &&
             (keyCode == kEscapeKeyCode || keyCode == kTabKeyCode));
 
   // 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
--- a/widget/src/cocoa/nsMenuBarX.h
+++ b/widget/src/cocoa/nsMenuBarX.h
@@ -115,16 +115,17 @@ public:
   void              UnregisterCommand(PRUint32 aCommandID);
   PRUint32          GetMenuCount();
   bool              MenuContainsAppMenu();
   nsMenuX*          GetMenuAt(PRUint32 aIndex);
   nsMenuItemX*      GetMenuItemForCommandID(PRUint32 inCommandID);
   nsresult          Paint();
   void              ForceUpdateNativeMenuAt(const nsAString& indexString);
   void              ForceNativeMenuReload(); // used for testing
+  static char       GetLocalizedAccelKey(char *shortcutID);
 
 protected:
   void              ConstructNativeMenus();
   nsresult          InsertMenuAtIndex(nsMenuX* aMenu, PRUint32 aIndex);
   void              RemoveMenuAtIndex(PRUint32 aIndex);
   nsChangeObserver* LookupContentChangeObserver(nsIContent* aContent);
   void              HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode);
   void              AquifyMenuBar();
--- a/widget/src/cocoa/nsMenuBarX.mm
+++ b/widget/src/cocoa/nsMenuBarX.mm
@@ -351,16 +351,55 @@ nsresult nsMenuBarX::Paint()
 
   gSomeMenuBarPainted = YES;
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
+// Returns the 'key' attribute of the 'shortcutID' object (if any) in the
+// currently active menubar's DOM document.  'shortcutID' should be the id
+// (i.e. the name) of a component that defines a commonly used (and
+// localized) cmd+key shortcut, and belongs to a keyset containing similar
+// objects.  For example "key_selectAll".  Returns a value that can be
+// compared to the first character of [NSEvent charactersIgnoringModifiers]
+// when [NSEvent modifierFlags] == NSCommandKeyMask.
+char nsMenuBarX::GetLocalizedAccelKey(char *shortcutID)
+{
+  if (!sLastGeckoMenuBarPainted)
+    return 0;
+
+  nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(sLastGeckoMenuBarPainted->mDocument));
+  if (!domDoc)
+    return 0;
+
+  NS_ConvertASCIItoUTF16 shortcutIDStr((const char *)shortcutID);
+  nsCOMPtr<nsIDOMElement> shortcutElement;
+  domDoc->GetElementById(shortcutIDStr, getter_AddRefs(shortcutElement));
+  nsCOMPtr<nsIContent> shortcutContent = do_QueryInterface(shortcutElement);
+  if (!shortcutContent)
+    return 0;
+
+  nsAutoString key;
+  shortcutContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::key, key);
+  NS_LossyConvertUTF16toASCII keyASC(key.get());
+  const char *keyASCPtr = keyASC.get();
+  if (!keyASCPtr)
+    return 0;
+  // If keyID's 'key' attribute isn't exactly one character long, it's not
+  // what we're looking for.
+  if (strlen(keyASCPtr) != sizeof(char))
+    return 0;
+  // Make sure retval is lower case.
+  char retval = tolower(keyASCPtr[0]);
+
+  return retval;
+}
+
 // 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));