Bug 359638 accesskeys are incorrectly shifted again (i.e. accesskey=. is broken) and also for b=398264, b=401086, b=414130, b=427797, b=427932, b=427995 r=karlt+ere+josh, sr=roc, a1.9=mconnor
authormasayuki@d-toybox.com
Mon, 14 Apr 2008 21:16:24 -0700
changeset 14328 ad45243a5cd950d25c98b0226427fa532948dbe2
parent 14327 548912d1e6951755bafd6a7e5bc005ad8ceaf909
child 14329 7c97942069e94e461d4cfbc1c95b1079b6f9a3a8
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt, roc
bugs359638, 398264, 401086, 414130, 427797, 427932, 427995
milestone1.9pre
Bug 359638 accesskeys are incorrectly shifted again (i.e. accesskey=. is broken) and also for b=398264, b=401086, b=414130, b=427797, b=427932, b=427995 r=karlt+ere+josh, sr=roc, a1.9=mconnor
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/events/src/nsEventStateManager.cpp
content/events/src/nsEventStateManager.h
content/xbl/src/nsXBLEventHandler.cpp
content/xbl/src/nsXBLEventHandler.h
content/xbl/src/nsXBLPrototypeHandler.cpp
content/xbl/src/nsXBLPrototypeHandler.h
content/xbl/src/nsXBLWindowKeyHandler.cpp
content/xbl/src/nsXBLWindowKeyHandler.h
layout/xul/base/src/nsMenuBarFrame.cpp
layout/xul/base/src/nsMenuBarListener.cpp
widget/public/nsGUIEvent.h
widget/src/cocoa/nsChildView.mm
widget/src/gtk2/nsNativeKeyBindings.cpp
widget/src/gtk2/nsNativeKeyBindings.h
widget/src/gtk2/nsWindow.cpp
widget/src/windows/nsKeyboardLayout.cpp
widget/src/windows/nsKeyboardLayout.h
widget/src/windows/nsWindow.cpp
widget/src/windows/nsWindow.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -52,16 +52,17 @@
 #include "nsContentList.h"
 #include "nsDOMClassInfoID.h"
 #include "nsIClassInfo.h"
 #include "nsIDOM3Node.h"
 #include "nsDataHashtable.h"
 #include "nsIScriptRuntime.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIDOMEvent.h"
+#include "nsTArray.h"
 
 struct nsNativeKeyEvent; // Don't include nsINativeKeyBindings.h here: it will force strange compilation error!
 
 class nsIDOMScriptObjectFactory;
 class nsIXPConnect;
 class nsINode;
 class nsIContent;
 class nsIDOMNode;
@@ -119,16 +120,25 @@ enum EventNameType {
   EventNameType_All = 0xFFFF
 };
 
 struct EventNameMapping {
   PRUint32  mId;
   PRInt32 mType;
 };
 
+struct nsShortcutCandidate {
+  nsShortcutCandidate(PRUint32 aCharCode, PRBool aIgnoreShift) :
+    mCharCode(aCharCode), mIgnoreShift(aIgnoreShift)
+  {
+  }
+  PRUint32 mCharCode;
+  PRBool   mIgnoreShift;
+};
+
 class nsContentUtils
 {
 public:
   static nsresult Init();
 
   // You MUST pass the old ownerDocument of aContent in as aOldDocument and the
   // new one as aNewDocument.  aNewParent is allowed to be null; in that case
   // aNewDocument will be assumed to be the parent.  Note that at this point
@@ -1154,16 +1164,36 @@ public:
    * See bug 406407 for details.
    */
   static nsEvent* GetNativeEvent(nsIDOMEvent* aDOMEvent);
   static PRBool DOMEventToNativeKeyEvent(nsIDOMEvent* aDOMEvent,
                                          nsNativeKeyEvent* aNativeEvent,
                                          PRBool aGetCharCode);
 
   /**
+   * Get the candidates for accelkeys for aDOMEvent.
+   *
+   * @param aDOMEvent [in] the input event for accelkey handling.
+   * @param aCandidates [out] the candidate shortcut key combination list.
+   *                          the first item is most preferred.
+   */
+  static void GetAccelKeyCandidates(nsIDOMEvent* aDOMEvent,
+                                    nsTArray<nsShortcutCandidate>& aCandidates);
+
+  /**
+   * Get the candidates for accesskeys for aDOMEvent.
+   *
+   * @param aNativeKeyEvent [in] the input event for accesskey handling.
+   * @param aCandidates [out] the candidate access key list.
+   *                          the first item is most preferred.
+   */
+  static void GetAccessKeyCandidates(nsKeyEvent* aNativeKeyEvent,
+                                     nsTArray<PRUint32>& aCandidates);
+
+  /**
    * Hide any XUL popups associated with aDocument, including any documents
    * displayed in child frames.
    */
   static void HidePopupsInDocument(nsIDocument* aDocument);
 
   /**
    * Return true if aURI is a local file URI (i.e. file://).
    */
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -3996,16 +3996,134 @@ nsContentUtils::DOMEventToNativeKeyEvent
 
   aNativeEvent->nativeEvent = GetNativeEvent(aDOMEvent);
 
   return PR_TRUE;
 }
 
 /* static */
 void
+nsContentUtils::GetAccelKeyCandidates(nsIDOMEvent* aDOMEvent,
+                  nsTArray<nsShortcutCandidate>& aCandidates)
+{
+  NS_PRECONDITION(aCandidates.IsEmpty(), "aCandidates must be empty");
+
+  nsAutoString eventType;
+  aDOMEvent->GetType(eventType);
+  // Don't process if aDOMEvent is not a keypress event.
+  if (!eventType.EqualsLiteral("keypress"))
+    return;
+
+  nsKeyEvent* nativeKeyEvent =
+    static_cast<nsKeyEvent*>(GetNativeEvent(aDOMEvent));
+  if (nativeKeyEvent) {
+    // nsShortcutCandidate::mCharCode is a candidate charCode.
+    // nsShoftcutCandidate::mIgnoreShift means the mCharCode should be tried to
+    // execute a command with/without shift key state. If this is TRUE, the
+    // shifted key state should be ignored. Otherwise, don't ignore the state.
+    // the priority of the charCodes are (shift key is not pressed):
+    //   0: charCode/PR_FALSE,
+    //   1: unshiftedCharCodes[0]/PR_FALSE, 2: unshiftedCharCodes[1]/PR_FALSE...
+    // the priority of the charCodes are (shift key is pressed):
+    //   0: charCode/PR_FALSE,
+    //   1: shiftedCharCodes[0]/PR_FALSE, 2: shiftedCharCodes[0]/PR_TRUE,
+    //   3: shiftedCharCodes[1]/PR_FALSE, 4: shiftedCharCodes[1]/PR_TRUE...
+    if (nativeKeyEvent->charCode) {
+      nsShortcutCandidate key(nativeKeyEvent->charCode, PR_FALSE);
+      aCandidates.AppendElement(key);
+    }
+
+    if (!nativeKeyEvent->isShift) {
+      for (PRUint32 i = 0;
+           i < nativeKeyEvent->alternativeCharCodes.Length(); ++i) {
+        PRUint32 ch =
+          nativeKeyEvent->alternativeCharCodes[0].mUnshiftedCharCode;
+        if (!ch || ch == nativeKeyEvent->charCode)
+          continue;
+
+        nsShortcutCandidate key(ch, PR_FALSE);
+        aCandidates.AppendElement(key);
+      }
+    } else {
+      for (PRUint32 i = 0;
+           i < nativeKeyEvent->alternativeCharCodes.Length(); ++i) {
+        PRUint32 ch = nativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode;
+        if (!ch)
+          continue;
+
+        if (ch != nativeKeyEvent->charCode) {
+          nsShortcutCandidate key(ch, PR_FALSE);
+          aCandidates.AppendElement(key);
+        }
+
+        // If the char is an alphabet, the shift key state should not be
+        // ignored. E.g., Ctrl+Shift+C should not execute Ctrl+C.
+        // And checking the charCode is same as unshiftedCharCode too.
+        // E.g., for Ctrl+Shift+(Plus of Numpad) should not run Ctrl+Plus.
+        PRUint32 unshiftCh =
+          nativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode;
+        if (ch == unshiftCh ||
+            (IS_IN_BMP(ch) && IS_IN_BMP(unshiftCh) &&
+             ToLowerCase(PRUnichar(ch)) == ToLowerCase(PRUnichar(unshiftCh))))
+          continue;
+
+        // Setting the alternative charCode candidates for retry without shift
+        // key state only when the shift key is pressed.
+        nsShortcutCandidate key(ch, PR_TRUE);
+        aCandidates.AppendElement(key);
+      }
+    }
+  } else {
+    nsCOMPtr<nsIDOMKeyEvent> key(do_QueryInterface(aDOMEvent));
+    PRUint32 charCode;
+    key->GetCharCode(&charCode);
+    if (charCode) {
+      nsShortcutCandidate key(charCode, PR_FALSE);
+      aCandidates.AppendElement(key);
+    }
+  }
+}
+
+/* static */
+void
+nsContentUtils::GetAccessKeyCandidates(nsKeyEvent* aNativeKeyEvent,
+                                       nsTArray<PRUint32>& aCandidates)
+{
+  NS_PRECONDITION(aCandidates.IsEmpty(), "aCandidates must be empty");
+
+  // return the lower cased charCode candidates for access keys.
+  // the priority of the charCodes are:
+  //   0: charCode, 1: unshiftedCharCodes[0], 2: shiftedCharCodes[0]
+  //   3: unshiftedCharCodes[1], 4: shiftedCharCodes[1],...
+  if (aNativeKeyEvent->charCode) {
+    PRUint32 ch = aNativeKeyEvent->charCode;
+    if (IS_IN_BMP(ch))
+      ch = ToLowerCase(PRUnichar(ch));
+    aCandidates.AppendElement(ch);
+  }
+  for (PRUint32 i = 0;
+       i < aNativeKeyEvent->alternativeCharCodes.Length(); ++i) {
+    PRUint32 ch[2] =
+      { aNativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode,
+        aNativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode };
+    for (PRUint32 j = 0; j < 2; ++j) {
+      if (!ch[j])
+        continue;
+      if (IS_IN_BMP(ch[j]))
+        ch[j] = ToLowerCase(PRUnichar(ch[j]));
+      // Don't append the charCode that was already appended.
+      if (aCandidates.IndexOf(ch[j]) == aCandidates.NoIndex)
+        aCandidates.AppendElement(ch[j]);
+    }
+  }
+  return;
+}
+
+/* static */
+void
 nsContentUtils::AddScriptBlocker()
 {
   if (!sScriptBlockerCount) {
     NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
                  "Should not already have a count");
     sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Count();
   }
   ++sScriptBlockerCount;
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -1436,17 +1436,17 @@ GetAccessModifierMask(nsISupports* aDocS
     return sContentAccessModifier;
 
   default:
     return -1; // invalid modifier
   }
 }
 
 static PRBool
-IsAccessKeyTarget(nsIContent* aContent, nsIFrame* aFrame, nsString& aKey)
+IsAccessKeyTarget(nsIContent* aContent, nsIFrame* aFrame, nsAString& aKey)
 {
   if (!aFrame)
     return PR_FALSE;
 
   if (!aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::accesskey, aKey, eIgnoreCase))
     return PR_FALSE;
 
   if (aFrame->IsFocusable())
@@ -1460,58 +1460,75 @@ IsAccessKeyTarget(nsIContent* aContent, 
 
   nsCOMPtr<nsIDOMXULControlElement> control(do_QueryInterface(aContent));
   if (control)
     return PR_TRUE;
 
   return PR_FALSE;
 }
 
+PRBool
+nsEventStateManager::ExecuteAccessKey(nsTArray<PRUint32>& aAccessCharCodes,
+                                      PRBool aIsTrustedEvent)
+{
+  PRInt32 count, start = -1;
+  if (mCurrentFocus) {
+    start = mAccessKeys.IndexOf(mCurrentFocus);
+    if (start == -1 && mCurrentFocus->GetBindingParent())
+      start = mAccessKeys.IndexOf(mCurrentFocus->GetBindingParent());
+  }
+  nsIContent *content;
+  nsIFrame *frame;
+  PRInt32 length = mAccessKeys.Count();
+  for (PRUint32 i = 0; i < aAccessCharCodes.Length(); ++i) {
+    PRUint32 ch = aAccessCharCodes[i];
+    nsAutoString accessKey;
+    AppendUCS4ToUTF16(ch, accessKey);
+    for (count = 1; count <= length; ++count) {
+      content = mAccessKeys[(start + count) % length];
+      frame = mPresContext->PresShell()->GetPrimaryFrameFor(content);
+      if (IsAccessKeyTarget(content, frame, accessKey)) {
+        PRBool shouldActivate = sKeyCausesActivation;
+        while (shouldActivate && ++count <= length) {
+          nsIContent *oc = mAccessKeys[(start + count) % length];
+          nsIFrame *of = mPresContext->PresShell()->GetPrimaryFrameFor(oc);
+          if (IsAccessKeyTarget(oc, of, accessKey))
+            shouldActivate = PR_FALSE;
+        }
+        if (shouldActivate)
+          content->PerformAccesskey(shouldActivate, aIsTrustedEvent);
+        else if (frame && frame->IsFocusable())
+          ChangeFocusWith(content, eEventFocusedByKey);
+        return PR_TRUE;
+      }
+    }
+  }
+  return PR_FALSE;
+}
+
 void
 nsEventStateManager::HandleAccessKey(nsPresContext* aPresContext,
                                      nsKeyEvent *aEvent,
                                      nsEventStatus* aStatus,
                                      nsIDocShellTreeItem* aBubbledFrom,
                                      ProcessingAccessKeyState aAccessKeyState,
                                      PRInt32 aModifierMask)
 {
   nsCOMPtr<nsISupports> pcContainer = aPresContext->GetContainer();
 
   // Alt or other accesskey modifier is down, we may need to do an accesskey
-  PRInt32 length = mAccessKeys.Count();
-  if (length > 0 && aModifierMask == GetAccessModifierMask(pcContainer)) {
+  if (mAccessKeys.Count() > 0 &&
+      aModifierMask == GetAccessModifierMask(pcContainer)) {
     // Someone registered an accesskey.  Find and activate it.
-    nsAutoString accKey(aEvent->charCode);
-    PRInt32 count, start = -1;
-    if (mCurrentFocus) {
-      start = mAccessKeys.IndexOf(mCurrentFocus);
-      if (start == -1 && mCurrentFocus->GetBindingParent())
-        start = mAccessKeys.IndexOf(mCurrentFocus->GetBindingParent());
-    }
-    nsIContent *content;
-    nsIFrame *frame;
-    for (count = 1; count <= length; ++count) {
-      content = mAccessKeys[(start + count) % length];
-      frame = mPresContext->PresShell()->GetPrimaryFrameFor(content);
-      if (IsAccessKeyTarget(content, frame, accKey)) {
-        PRBool shouldActivate = sKeyCausesActivation;
-        while (shouldActivate && ++count <= length) {
-          nsIContent *oc = mAccessKeys[(start + count) % length];
-          nsIFrame *of = mPresContext->PresShell()->GetPrimaryFrameFor(oc);
-          if (IsAccessKeyTarget(oc, of, accKey))
-            shouldActivate = PR_FALSE;
-        }
-        if (shouldActivate)
-          content->PerformAccesskey(shouldActivate,
-                                    NS_IS_TRUSTED_EVENT(aEvent));
-        else if (frame && frame->IsFocusable())
-          ChangeFocusWith(content, eEventFocusedByKey);
-        *aStatus = nsEventStatus_eConsumeNoDefault;
-        break;
-      }
+    PRBool isTrusted = NS_IS_TRUSTED_EVENT(aEvent);
+    nsAutoTArray<PRUint32, 10> accessCharCodes;
+    nsContentUtils::GetAccessKeyCandidates(aEvent, accessCharCodes);
+    if (ExecuteAccessKey(accessCharCodes, isTrusted)) {
+      *aStatus = nsEventStatus_eConsumeNoDefault;
+      return;
     }
   }
 
   // after the local accesskey handling
   if (nsEventStatus_eConsumeNoDefault != *aStatus) {
     // checking all sub docshells
 
     nsCOMPtr<nsIDocShellTreeNode> docShell(do_QueryInterface(pcContainer));
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -263,16 +263,19 @@ protected:
    */
   void HandleAccessKey(nsPresContext* aPresContext,
                        nsKeyEvent* aEvent,
                        nsEventStatus* aStatus,
                        nsIDocShellTreeItem* aBubbledFrom,
                        ProcessingAccessKeyState aAccessKeyState,
                        PRInt32 aModifierMask);
 
+  PRBool ExecuteAccessKey(nsTArray<PRUint32>& aAccessCharCodes,
+                          PRBool aIsTrustedEvent);
+
   //---------------------------------------------
   // DocShell Focus Traversal Methods
   //---------------------------------------------
 
   nsresult ShiftFocusInternal(PRBool aForward, nsIContent* aStart = nsnull);
   void TabIntoDocument(nsIDocShell* aDocShell, PRBool aForward);
   void ShiftFocusByDoc(PRBool forward);
   PRBool IsFrameSetDoc(nsIDocShell* aDocShell);
--- a/content/xbl/src/nsXBLEventHandler.cpp
+++ b/content/xbl/src/nsXBLEventHandler.cpp
@@ -44,17 +44,19 @@
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMText.h"
 #include "nsIDOM3EventTarget.h"
 #include "nsGkAtoms.h"
 #include "nsXBLPrototypeHandler.h"
 #include "nsIDOMNSEvent.h"
+#include "nsGUIEvent.h"
 #include "nsContentUtils.h"
+#include "nsUnicharUtils.h"
 
 nsXBLEventHandler::nsXBLEventHandler(nsXBLPrototypeHandler* aHandler)
   : mProtoHandler(aHandler)
 {
 }
 
 nsXBLEventHandler::~nsXBLEventHandler()
 {
@@ -114,55 +116,75 @@ nsXBLKeyEventHandler::nsXBLKeyEventHandl
 }
 
 nsXBLKeyEventHandler::~nsXBLKeyEventHandler()
 {
 }
 
 NS_IMPL_ISUPPORTS1(nsXBLKeyEventHandler, nsIDOMEventListener)
 
+PRBool
+nsXBLKeyEventHandler::ExecuteMatchedHandlers(nsIDOMKeyEvent* aKeyEvent,
+                                             PRUint32 aCharCode,
+                                             PRBool aIgnoreShiftKey)
+{
+  nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aKeyEvent);
+  PRBool trustedEvent = PR_FALSE;
+  if (domNSEvent)
+    domNSEvent->GetIsTrusted(&trustedEvent);
+
+  nsCOMPtr<nsIDOMEventTarget> target;
+  aKeyEvent->GetCurrentTarget(getter_AddRefs(target));
+  nsCOMPtr<nsPIDOMEventTarget> piTarget = do_QueryInterface(target);
+
+  PRBool executed = PR_FALSE;
+  for (PRUint32 i = 0; i < mProtoHandlers.Count(); ++i) {
+    nsXBLPrototypeHandler* handler = static_cast<nsXBLPrototypeHandler*>
+                                                (mProtoHandlers[i]);
+    PRBool hasAllowUntrustedAttr = handler->HasAllowUntrustedAttr();
+    if ((trustedEvent ||
+        (hasAllowUntrustedAttr && handler->AllowUntrustedEvents()) ||
+        (!hasAllowUntrustedAttr && !mIsBoundToChrome)) &&
+        handler->KeyEventMatched(aKeyEvent, aCharCode, aIgnoreShiftKey)) {
+      handler->ExecuteHandler(piTarget, aKeyEvent);
+      executed = PR_TRUE;
+    }
+  }
+  return executed;
+}
+
 NS_IMETHODIMP
 nsXBLKeyEventHandler::HandleEvent(nsIDOMEvent* aEvent)
 {
   PRUint32 count = mProtoHandlers.Count();
   if (count == 0)
     return NS_ERROR_FAILURE;
 
   if (mPhase == NS_PHASE_TARGET) {
     PRUint16 eventPhase;
     aEvent->GetEventPhase(&eventPhase);
     if (eventPhase != nsIDOMEvent::AT_TARGET)
       return NS_OK;
   }
 
-  nsCOMPtr<nsIDOMEventTarget> target;
-  aEvent->GetCurrentTarget(getter_AddRefs(target));
-  nsCOMPtr<nsPIDOMEventTarget> piTarget = do_QueryInterface(target);
-
   nsCOMPtr<nsIDOMKeyEvent> key(do_QueryInterface(aEvent));
 
-  nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aEvent);
-  PRBool trustedEvent = PR_FALSE;
-  if (domNSEvent) {
-    domNSEvent->GetIsTrusted(&trustedEvent);
+  nsAutoTArray<nsShortcutCandidate, 10> accessKeys;
+  nsContentUtils::GetAccelKeyCandidates(aEvent, accessKeys);
+
+  if (accessKeys.IsEmpty()) {
+    ExecuteMatchedHandlers(key, 0, PR_FALSE);
+    return NS_OK;
   }
 
-  PRUint32 i;
-  for (i = 0; i < count; ++i) {
-    nsXBLPrototypeHandler* handler = static_cast<nsXBLPrototypeHandler*>
-                                                (mProtoHandlers[i]);
-    PRBool hasAllowUntrustedAttr = handler->HasAllowUntrustedAttr();
-    if ((trustedEvent ||
-        (hasAllowUntrustedAttr && handler->AllowUntrustedEvents()) ||
-        (!hasAllowUntrustedAttr && !mIsBoundToChrome)) &&
-        handler->KeyEventMatched(key)) {
-      handler->ExecuteHandler(piTarget, aEvent);
-    }
+  for (PRUint32 i = 0; i < accessKeys.Length(); ++i) {
+    if (ExecuteMatchedHandlers(key, accessKeys[i].mCharCode,
+                               accessKeys[i].mIgnoreShift))
+      return NS_OK;
   }
-
   return NS_OK;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////
 
 nsresult
 NS_NewXBLEventHandler(nsXBLPrototypeHandler* aHandler,
                       nsIAtom* aEventType,
--- a/content/xbl/src/nsXBLEventHandler.h
+++ b/content/xbl/src/nsXBLEventHandler.h
@@ -41,16 +41,17 @@
 
 #include "nsCOMPtr.h"
 #include "nsIDOMEventListener.h"
 #include "nsVoidArray.h"
 
 class nsIAtom;
 class nsIContent;
 class nsIDOM3EventTarget;
+class nsIDOMKeyEvent;
 class nsPIDOMEventTarget;
 class nsXBLPrototypeHandler;
 
 class nsXBLEventHandler : public nsIDOMEventListener
 {
 public:
   nsXBLEventHandler(nsXBLPrototypeHandler* aHandler);
   virtual ~nsXBLEventHandler();
@@ -116,16 +117,18 @@ public:
   }
 
   void SetIsBoundToChrome(PRBool aIsBoundToChrome)
   {
     mIsBoundToChrome = aIsBoundToChrome;
   }
 private:
   nsXBLKeyEventHandler();
+  PRBool ExecuteMatchedHandlers(nsIDOMKeyEvent* aEvent, PRUint32 aCharCode,
+                                PRBool aIgnoreShiftKey);
 
   nsVoidArray mProtoHandlers;
   nsCOMPtr<nsIAtom> mEventType;
   PRUint8 mPhase;
   PRUint8 mType;
   PRPackedBool mIsBoundToChrome;
 };
 
--- a/content/xbl/src/nsXBLPrototypeHandler.cpp
+++ b/content/xbl/src/nsXBLPrototypeHandler.cpp
@@ -591,35 +591,41 @@ nsXBLPrototypeHandler::GetController(nsP
     controllers->GetControllerAt(0, &controller);  // return reference
   }
   else controller = nsnull;
 
   return controller;
 }
 
 PRBool
-nsXBLPrototypeHandler::KeyEventMatched(nsIDOMKeyEvent* aKeyEvent)
+nsXBLPrototypeHandler::KeyEventMatched(nsIDOMKeyEvent* aKeyEvent,
+                                       PRUint32 aCharCode,
+                                       PRBool aIgnoreShiftKey)
 {
   if (mDetail == -1)
     return PR_TRUE; // No filters set up. It's generic.
 
   // Get the keycode or charcode of the key event.
   PRUint32 code;
 
   if (mMisc) {
-    aKeyEvent->GetCharCode(&code);
-    code = ToLowerCase(PRUnichar(code));
+    if (aCharCode)
+      code = aCharCode;
+    else
+      aKeyEvent->GetCharCode(&code);
+    if (IS_IN_BMP(code))
+      code = ToLowerCase(PRUnichar(code));
   }
   else
     aKeyEvent->GetKeyCode(&code);
 
   if (code != PRUint32(mDetail))
     return PR_FALSE;
 
-  return ModifiersMatchMask(aKeyEvent);
+  return ModifiersMatchMask(aKeyEvent, aIgnoreShiftKey);
 }
 
 PRBool
 nsXBLPrototypeHandler::MouseEventMatched(nsIDOMMouseEvent* aMouseEvent)
 {
   if (mDetail == -1 && mMisc == 0 && (mKeyMask & cAllModifiers) == 0)
     return PR_TRUE; // No filters set up. It's generic.
 
@@ -987,29 +993,30 @@ nsXBLPrototypeHandler::ReportKeyConflict
                                   aMessageName,
                                   params, NS_ARRAY_LENGTH(params),
                                   uri, EmptyString(), mLineNumber, 0,
                                   nsIScriptError::warningFlag,
                                   "XBL Prototype Handler");
 }
 
 PRBool
-nsXBLPrototypeHandler::ModifiersMatchMask(nsIDOMUIEvent* aEvent)
+nsXBLPrototypeHandler::ModifiersMatchMask(nsIDOMUIEvent* aEvent,
+                                          PRBool aIgnoreShiftKey)
 {
   nsCOMPtr<nsIDOMKeyEvent> key(do_QueryInterface(aEvent));
   nsCOMPtr<nsIDOMMouseEvent> mouse(do_QueryInterface(aEvent));
 
   PRBool keyPresent;
   if (mKeyMask & cMetaMask) {
     key ? key->GetMetaKey(&keyPresent) : mouse->GetMetaKey(&keyPresent);
     if (keyPresent != ((mKeyMask & cMeta) != 0))
       return PR_FALSE;
   }
 
-  if (mKeyMask & cShiftMask) {
+  if (mKeyMask & cShiftMask && !aIgnoreShiftKey) {
     key ? key->GetShiftKey(&keyPresent) : mouse->GetShiftKey(&keyPresent);
     if (keyPresent != ((mKeyMask & cShift) != 0))
       return PR_FALSE;
   }
 
   if (mKeyMask & cAltMask) {
     key ? key->GetAltKey(&keyPresent) : mouse->GetAltKey(&keyPresent);
     if (keyPresent != ((mKeyMask & cAlt) != 0))
--- a/content/xbl/src/nsXBLPrototypeHandler.h
+++ b/content/xbl/src/nsXBLPrototypeHandler.h
@@ -86,24 +86,29 @@ public:
                         nsXBLPrototypeBinding* aBinding,
                         PRUint32 aLineNumber);
 
   // This constructor is used only by XUL key handlers (e.g., <key>)
   nsXBLPrototypeHandler(nsIContent* aKeyElement);
 
   ~nsXBLPrototypeHandler();
 
-  PRBool KeyEventMatched(nsIDOMKeyEvent* aKeyEvent);
+  // if aCharCode is not zero, it is used instead of the charCode of aKeyEvent.
+  PRBool KeyEventMatched(nsIDOMKeyEvent* aKeyEvent,
+                         PRUint32 aCharCode = 0,
+                         PRBool aIgnoreShiftKey = PR_FALSE);
   inline PRBool KeyEventMatched(nsIAtom* aEventType,
-                                nsIDOMKeyEvent* aEvent)
+                                nsIDOMKeyEvent* aEvent,
+                                PRUint32 aCharCode = 0,
+                                PRBool aIgnoreShiftKey = PR_FALSE)
   {
     if (aEventType != mEventName)
       return PR_FALSE;
 
-    return KeyEventMatched(aEvent);
+    return KeyEventMatched(aEvent, aCharCode, aIgnoreShiftKey);
   }
 
   PRBool MouseEventMatched(nsIDOMMouseEvent* aMouseEvent);
   inline PRBool MouseEventMatched(nsIAtom* aEventType,
                                   nsIDOMMouseEvent* aEvent)
   {
     if (aEventType != mEventName)
       return PR_FALSE;
@@ -166,17 +171,18 @@ protected:
                           const PRUnichar* aKeyCode=nsnull, const PRUnichar* aCharCode=nsnull,
                           const PRUnichar* aModifiers=nsnull, const PRUnichar* aButton=nsnull,
                           const PRUnichar* aClickCount=nsnull, const PRUnichar* aGroup=nsnull,
                           const PRUnichar* aPreventDefault=nsnull,
                           const PRUnichar* aAllowUntrusted=nsnull);
 
   void ReportKeyConflict(const PRUnichar* aKey, const PRUnichar* aModifiers, nsIContent* aElement, const char *aMessageName);
   void GetEventType(nsAString& type);
-  PRBool ModifiersMatchMask(nsIDOMUIEvent* aEvent);
+  PRBool ModifiersMatchMask(nsIDOMUIEvent* aEvent,
+                            PRBool aIgnoreShiftKey = PR_FALSE);
   nsresult DispatchXBLCommand(nsPIDOMEventTarget* aTarget, nsIDOMEvent* aEvent);
   nsresult DispatchXULKeyCommand(nsIDOMEvent* aEvent);
   nsresult EnsureEventHandler(nsIScriptGlobalObject* aGlobal,
                               nsIScriptContext *aBoundContext, nsIAtom *aName,
                               nsScriptObjectHolder &aHandler);
   static PRInt32 KeyToMask(PRInt32 key);
   
   static PRInt32 kAccelKey;
--- a/content/xbl/src/nsXBLWindowKeyHandler.cpp
+++ b/content/xbl/src/nsXBLWindowKeyHandler.cpp
@@ -65,16 +65,17 @@
 #include "nsIDOMNSDocument.h"
 #include "nsPIWindowRoot.h"
 #include "nsPIDOMWindow.h"
 #include "nsIFocusController.h"
 #include "nsIDocShell.h"
 #include "nsIPresShell.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsISelectionController.h"
+#include "nsGUIEvent.h"
 
 static nsINativeKeyBindings *sNativeEditorBindings = nsnull;
 
 class nsXBLSpecialDocInfo
 {
 public:
   nsCOMPtr<nsIXBLDocumentInfo> mHTMLBindings;
   nsCOMPtr<nsIXBLDocumentInfo> mUserHTMLBindings;
@@ -211,16 +212,26 @@ BuildHandlerChain(nsIContent* aContent, 
 
   // Since we chain each handler onto the next handler,
   // we'll enumerate them here in reverse so that when we
   // walk the chain they'll come out in the original order
   for (PRUint32 j = aContent->GetChildCount(); j--; ) {
     nsIContent *key = aContent->GetChildAt(j);
 
     if (key->NodeInfo()->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
+      // Check whether the key element has empty value at key/char attribute.
+      // Such element is used by localizers for alternative shortcut key
+      // definition on the locale. See bug 426501.
+      nsAutoString valKey, valChar;
+      PRBool attrExists =
+               key->GetAttr(kNameSpaceID_None, nsGkAtoms::key, valKey) ||
+               key->GetAttr(kNameSpaceID_None, nsGkAtoms::charcode, valChar);
+      if (attrExists && valKey.IsEmpty() && valChar.IsEmpty())
+        continue;
+
       nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(key);
 
       if (!handler)
         return;
 
       handler->SetNextHandler(*aResult);
       *aResult = handler;
     }
@@ -400,21 +411,23 @@ nsresult nsXBLWindowKeyHandler::KeyPress
 
 //
 // EventMatched
 //
 // See if the given handler cares about this particular key event
 //
 PRBool
 nsXBLWindowKeyHandler::EventMatched(nsXBLPrototypeHandler* inHandler,
-                                    nsIAtom* inEventType, nsIDOMEvent* inEvent)
+                                    nsIAtom* inEventType, nsIDOMEvent* inEvent,
+                                    PRUint32 aCharCode, PRBool aIgnoreShiftKey)
 {
   nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(inEvent));
   if (keyEvent)
-    return inHandler->KeyEventMatched(inEventType, keyEvent);
+    return inHandler->KeyEventMatched(inEventType, keyEvent, aCharCode,
+                                      aIgnoreShiftKey);
 
   return PR_FALSE;
 }
 
 /* static */ void
 nsXBLWindowKeyHandler::ShutDown()
 {
   NS_IF_RELEASE(sNativeEditorBindings);
@@ -453,41 +466,66 @@ nsXBLWindowKeyHandler::IsEditor()
     presShell->GetSelectionFlags(&isEditor);
     return isEditor == nsISelectionDisplay::DISPLAY_ALL;
   }
 
   return PR_FALSE;
 }
 
 //
-// WalkHandlersInternal
+// WalkHandlersInternal and WalkHandlersAndExecute
 //
 // Given a particular DOM event and a pointer to the first handler in the list,
 // scan through the list to find something to handle the event and then make it
 // so.
 //
 nsresult
 nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMEvent* aEvent,
                                             nsIAtom* aEventType, 
                                             nsXBLPrototypeHandler* aHandler)
 {
+  nsAutoTArray<nsShortcutCandidate, 10> accessKeys;
+  nsContentUtils::GetAccelKeyCandidates(aEvent, accessKeys);
+
+  if (accessKeys.IsEmpty()) {
+    WalkHandlersAndExecute(aEvent, aEventType, aHandler, 0, PR_FALSE);
+    return NS_OK;
+  }
+
+  for (PRUint32 i = 0; i < accessKeys.Length(); ++i) {
+    nsShortcutCandidate &key = accessKeys[i];
+    if (WalkHandlersAndExecute(aEvent, aEventType, aHandler,
+                               key.mCharCode, key.mIgnoreShift))
+      return NS_OK;
+  }
+  return NS_OK;
+}
+
+PRBool
+nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMEvent* aEvent,
+                                              nsIAtom* aEventType,
+                                              nsXBLPrototypeHandler* aHandler,
+                                              PRUint32 aCharCode,
+                                              PRBool aIgnoreShiftKey)
+{
   nsresult rv;
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aEvent));
-  
+
   // Try all of the handlers until we find one that matches the event.
   for (nsXBLPrototypeHandler *currHandler = aHandler; currHandler;
        currHandler = currHandler->GetNextHandler()) {
     PRBool stopped;
     privateEvent->IsDispatchStopped(&stopped);
     if (stopped) {
       // The event is finished, don't execute any more handlers
       return NS_OK;
     }
 
-    if (!EventMatched(currHandler, aEventType, aEvent))
+    if (!EventMatched(currHandler, aEventType, aEvent,
+                      aCharCode, aIgnoreShiftKey))
       continue;  // try the next one
 
     // Before executing this handler, check that it's not disabled,
     // and that it has something to do (oncommand of the <key> or its
     // <command> is non-empty).
     nsCOMPtr<nsIContent> elt = currHandler->GetHandlerElement();
     nsCOMPtr<nsIDOMElement> commandElt;
 
@@ -536,21 +574,21 @@ nsXBLWindowKeyHandler::WalkHandlersInter
     if (element) {
       piTarget = do_QueryInterface(commandElt);
     } else {
       piTarget = mTarget;
     }
 
     rv = currHandler->ExecuteHandler(piTarget, aEvent);
     if (NS_SUCCEEDED(rv)) {
-      return NS_OK;
+      return PR_TRUE;
     }
   }
 
-  return NS_OK;
+  return PR_FALSE;
 }
 
 already_AddRefed<nsIDOMElement>
 nsXBLWindowKeyHandler::GetElement()
 {
   nsCOMPtr<nsIDOMElement> element = do_QueryReferent(mWeakPtrForElement);
   nsIDOMElement* el = nsnull;
   element.swap(el);
--- a/content/xbl/src/nsXBLWindowKeyHandler.h
+++ b/content/xbl/src/nsXBLWindowKeyHandler.h
@@ -75,23 +75,29 @@ public:
 protected:
   nsresult WalkHandlers(nsIDOMEvent* aKeyEvent, nsIAtom* aEventType);
 
   // walk the handlers, looking for one to handle the event
   nsresult WalkHandlersInternal(nsIDOMEvent* aKeyEvent,
                                 nsIAtom* aEventType, 
                                 nsXBLPrototypeHandler* aHandler);
 
+  // walk the handlers for aEvent, aCharCode and aIgnoreShiftKey
+  PRBool WalkHandlersAndExecute(nsIDOMEvent* aEvent, nsIAtom* aEventType,
+                                nsXBLPrototypeHandler* aHandler,
+                                PRUint32 aCharCode, PRBool aIgnoreShiftKey);
+
   // lazily load the handlers. Overridden to handle being attached
   // to a particular element rather than the document
   nsresult EnsureHandlers(PRBool *aIsEditor);
 
   // check if the given handler cares about the given key event
   PRBool EventMatched(nsXBLPrototypeHandler* inHandler, nsIAtom* inEventType,
-                      nsIDOMEvent* inEvent);
+                      nsIDOMEvent* inEvent, PRUint32 aCharCode,
+                      PRBool aIgnoreShiftKey);
 
   // are we working with editor or browser?
   PRBool IsEditor() ;
 
   // Returns the element which was passed as a parameter to the constructor,
   // unless the element has been removed from the document.
   already_AddRefed<nsIDOMElement> GetElement();
   // Using weak pointer to the DOM Element.
--- a/layout/xul/base/src/nsMenuBarFrame.cpp
+++ b/layout/xul/base/src/nsMenuBarFrame.cpp
@@ -59,16 +59,18 @@
 #include "nsIDOMDocument.h"
 #include "nsPIDOMWindow.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsCSSFrameConstructor.h"
 #ifdef XP_WIN
 #include "nsISound.h"
 #include "nsWidgetsCID.h"
 #endif
+#include "nsContentUtils.h"
+#include "nsUTF8Utils.h"
 
 
 //
 // NS_NewMenuBarFrame
 //
 // Wrapper for creating a new menu Bar container
 //
 nsIFrame*
@@ -204,48 +206,67 @@ GetInsertionPoint(nsIPresShell* aShell, 
   aShell->FrameConstructor()->GetInsertionPoint(aFrame, child, aResult);
 }
 
 nsMenuFrame*
 nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
 {
   PRUint32 charCode;
   aKeyEvent->GetCharCode(&charCode);
-  if (!charCode) // no character was pressed so just return  
-    return nsnull;
+
+  nsAutoTArray<PRUint32, 10> accessKeys;
+  nsEvent* nativeEvent = nsContentUtils::GetNativeEvent(aKeyEvent);
+  nsKeyEvent* nativeKeyEvent = static_cast<nsKeyEvent*>(nativeEvent);
+  if (nativeKeyEvent)
+    nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, accessKeys);
+  if (accessKeys.IsEmpty() && charCode)
+    accessKeys.AppendElement(charCode);
+
+  if (accessKeys.IsEmpty())
+    return nsnull; // no character was pressed so just return
 
   // Enumerate over our list of frames.
   nsIFrame* immediateParent = nsnull;
   GetInsertionPoint(PresContext()->PresShell(), this, nsnull, &immediateParent);
   if (!immediateParent)
     immediateParent = this;
 
+  // Find a most preferred accesskey which should be returned.
+  nsIFrame* foundMenu = nsnull;
+  PRUint32 foundIndex = accessKeys.NoIndex;
   nsIFrame* currFrame = immediateParent->GetFirstChild(nsnull);
 
   while (currFrame) {
     nsIContent* current = currFrame->GetContent();
-    
+
     // See if it's a menu item.
     if (nsXULPopupManager::IsValidMenuItem(PresContext(), current, PR_FALSE)) {
       // Get the shortcut attribute.
       nsAutoString shortcutKey;
       current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, shortcutKey);
       if (!shortcutKey.IsEmpty()) {
-        // We've got something.
-        PRUnichar letter = PRUnichar(charCode); // throw away the high-zero-fill
-        if ( shortcutKey.Equals(Substring(&letter, &letter+1),
-                                nsCaseInsensitiveStringComparator()) )  {
-          // We match!
-          return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
-                 static_cast<nsMenuFrame *>(currFrame) : nsnull;
+        ToLowerCase(shortcutKey);
+        nsAutoString::const_iterator start, end;
+        shortcutKey.BeginReading(start);
+        shortcutKey.EndReading(end);
+        PRUint32 ch = UTF16CharEnumerator::NextChar(start, end);
+        PRUint32 index = accessKeys.IndexOf(ch);
+        if (index != accessKeys.NoIndex &&
+            (foundIndex == kNotFound || index < foundIndex)) {
+          foundMenu = currFrame;
+          foundIndex = index;
         }
       }
     }
     currFrame = currFrame->GetNextSibling();
   }
+  if (foundMenu) {
+    return (foundMenu->GetType() == nsGkAtoms::menuFrame) ?
+           static_cast<nsMenuFrame *>(foundMenu) : nsnull;
+  }
 
   // didn't find a matching menu item
 #ifdef XP_WIN
   // behavior on Windows - this item is on the menu bar, beep and deactivate the menu bar
   if (mIsActive) {
     nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
     if (soundInterface)
       soundInterface->Beep();
--- a/layout/xul/base/src/nsMenuBarListener.cpp
+++ b/layout/xul/base/src/nsMenuBarListener.cpp
@@ -229,24 +229,32 @@ nsMenuBarListener::KeyPress(nsIDOMEvent*
 
     nsUIEvent->GetPreventDefault(&preventDefault);
     if (!preventDefault) {
       nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
       PRUint32 keyCode, charCode;
       keyEvent->GetKeyCode(&keyCode);
       keyEvent->GetCharCode(&charCode);
 
+      PRBool hasAccessKeyCandidates = charCode != 0;
+      if (!hasAccessKeyCandidates) {
+        nsEvent* nativeEvent = nsContentUtils::GetNativeEvent(aKeyEvent);
+        nsKeyEvent* nativeKeyEvent = static_cast<nsKeyEvent*>(nativeEvent);
+        if (nativeKeyEvent) {
+          nsAutoTArray<PRUint32, 10> keys;
+          nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, keys);
+          hasAccessKeyCandidates = !keys.IsEmpty();
+        }
+      }
+
       // Clear the access key flag unless we are pressing the access key.
       if (keyCode != (PRUint32)mAccessKey)
         mAccessKeyDown = PR_FALSE;
 
-      // If charCode == 0, then it is not a printable character.
-      // Don't attempt to handle accesskey for non-printable characters.
-      if (IsAccessKeyPressed(keyEvent) && charCode)
-      {
+      if (IsAccessKeyPressed(keyEvent) && hasAccessKeyCandidates) {
         // Do shortcut navigation.
         // A letter was pressed. We want to see if a shortcut gets matched. If
         // so, we'll know the menu got activated.
         nsMenuFrame* result = mMenuBarFrame->FindMenuWithShortcut(keyEvent);
         if (result) {
           mMenuBarFrame->SetActive(PR_TRUE);
           result->OpenMenu(PR_TRUE);
           aKeyEvent->StopPropagation();
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -51,16 +51,17 @@
 #ifdef WIN32
 #undef ERROR
 #endif
 #include "nsCOMPtr.h"
 #include "nsIAtom.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsWeakPtr.h"
 #include "nsIWidget.h"
+#include "nsTArray.h"
 
 class nsIRenderingContext;
 class nsIRegion;
 class nsIMenuItem;
 class nsIAccessible;
 class nsIContent;
 class nsIURI;
 class nsIDOMEvent;
@@ -695,29 +696,42 @@ public:
 
   nsIAccessible*  accessible;     
 };
 
 /**
  * Keyboard event
  */
 
+struct nsAlternativeCharCode {
+  nsAlternativeCharCode(PRUint32 aUnshiftedCharCode,
+                        PRUint32 aShiftedCharCode) :
+    mUnshiftedCharCode(aUnshiftedCharCode), mShiftedCharCode(aShiftedCharCode)
+  {
+  }
+  PRUint32 mUnshiftedCharCode;
+  PRUint32 mShiftedCharCode;
+};
+
 class nsKeyEvent : public nsInputEvent
 {
 public:
   nsKeyEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
     : nsInputEvent(isTrusted, msg, w, NS_KEY_EVENT),
       keyCode(0), charCode(0), isChar(0)
   {
   }
 
   /// see NS_VK codes
   PRUint32        keyCode;   
   /// OS translated Unicode char
   PRUint32        charCode;
+  // OS translated Unicode chars which are used for accesskey and accelkey
+  // handling. The handlers will try from first character to last character.
+  nsTArray<nsAlternativeCharCode> alternativeCharCodes;
   // indicates whether the event signifies a printable character
   PRBool          isChar;
 };
 
 /**
  * IME Related Events
  */
 struct nsTextRange
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -3874,27 +3874,83 @@ static PRBool IsNormalCharInputtingEvent
   // Check to see if the message is a key press that does not involve
   // one of our special key codes.
   if (outGeckoEvent->message == NS_KEY_PRESS && !IsSpecialGeckoKey([aKeyEvent keyCode])) {
     outGeckoEvent->isChar = PR_TRUE; // this is not a special key
     
     outGeckoEvent->charCode = 0;
     outGeckoEvent->keyCode  = 0; // not set for key press events
     
-    NSString* unmodifiedChars = [aKeyEvent charactersIgnoringModifiers];
-    if ([unmodifiedChars length] > 0)
-      outGeckoEvent->charCode = [unmodifiedChars characterAtIndex:0];
+    NSString* chars = [aKeyEvent characters];
+    if ([chars length] > 0)
+      outGeckoEvent->charCode = [chars characterAtIndex:0];
     
     // convert control-modified charCode to raw charCode (with appropriate case)
     if (outGeckoEvent->isControl && outGeckoEvent->charCode <= 26)
       outGeckoEvent->charCode += (outGeckoEvent->isShift) ? ('A' - 1) : ('a' - 1);
     
-    // gecko also wants charCode to be in the appropriate case
-    if (outGeckoEvent->isShift && (outGeckoEvent->charCode >= 'a' && outGeckoEvent->charCode <= 'z'))
-      outGeckoEvent->charCode -= 32; // convert to uppercase
+    // If Ctrl or Command is pressed, we should set shiftCharCode and
+    // unshiftCharCode for accessKeys and accelKeys.
+    if ((outGeckoEvent->isControl || outGeckoEvent->isMeta) &&
+        !outGeckoEvent->isAlt) {
+      SInt16 keyLayoutID =
+        ::GetScriptVariable(::GetScriptManagerVariable(smKeyScript),
+                            smScriptKeys);
+      Handle handle = ::GetResource('uchr', keyLayoutID);
+      PRUint32 unshiftedChar = 0;
+      PRUint32 shiftedChar = 0;
+      PRUint32 shiftedCmdChar = 0;
+      if (handle) {
+        UInt32 kbType = ::LMGetKbdType();
+        UInt32 deadKeyState = 0;
+        UniCharCount len;
+        UniChar chars[1];
+        OSStatus err;
+        err = ::UCKeyTranslate((UCKeyboardLayout*)*handle,
+                               [aKeyEvent keyCode],
+                               kUCKeyActionDown, 0,
+                               kbType, 0, &deadKeyState, 1, &len, chars);
+        if (noErr == err && len > 0)
+          unshiftedChar = chars[0];
+        deadKeyState = 0;
+        err = ::UCKeyTranslate((UCKeyboardLayout*)*handle, [aKeyEvent keyCode],
+                               kUCKeyActionDown, shiftKey >> 8,
+                               kbType, 0, &deadKeyState, 1, &len, chars);
+        if (noErr == err && len > 0)
+          shiftedChar = chars[0];
+        deadKeyState = 0;
+        err = ::UCKeyTranslate((UCKeyboardLayout*)*handle, [aKeyEvent keyCode],
+                               kUCKeyActionDown, (cmdKey | shiftKey) >> 8,
+                               kbType, 0, &deadKeyState, 1, &len, chars);
+        if (noErr == err && len > 0)
+          shiftedCmdChar = chars[0];
+      } else if (handle = (char**)::GetScriptManagerVariable(smKCHRCache)) {
+        UInt32 state = 0;
+        UInt32 keyCode = [aKeyEvent keyCode];
+        unshiftedChar = ::KeyTranslate(handle, keyCode, &state) & charCodeMask;
+        keyCode = [aKeyEvent keyCode] | shiftKey;
+        shiftedChar = ::KeyTranslate(handle, keyCode, &state) & charCodeMask;
+        keyCode = [aKeyEvent keyCode] | shiftKey | cmdKey;
+        shiftedCmdChar = ::KeyTranslate(handle, keyCode, &state) & charCodeMask;        
+      }
+      // If the current keyboad layout is switchable by Cmd key
+      // (e.g., Dvorak-QWERTY layout), we should not append the alternative
+      // char codes to unshiftedCharCodes and shiftedCharCodes.
+      // Because then, the alternative char codes might execute wrong item.
+      // Therefore, we should check whether the unshiftedChar and shiftedCmdChar
+      // are same. Because Cmd+Shift+'foo' returns unshifted 'foo'. So, they
+      // should be same for this case.
+      // Note that we cannot support the combination of Cmd and Shift needed
+      // char. (E.g., Cmd++ in US keyboard layout.)
+      if ((unshiftedChar || shiftedChar) &&
+          (!outGeckoEvent->isMeta || unshiftedChar == shiftedCmdChar)) {
+        nsAlternativeCharCode altCharCodes(unshiftedChar, shiftedChar);
+        outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
+      }
+    }
   }
   else {
     NSString* characters = nil;
     if ([aKeyEvent type] != NSFlagsChanged)
       characters = [aKeyEvent charactersIgnoringModifiers];
     
     outGeckoEvent->keyCode = ConvertMacToGeckoKeyCode([aKeyEvent keyCode], outGeckoEvent, characters);
     outGeckoEvent->charCode = 0;
--- a/widget/src/gtk2/nsNativeKeyBindings.cpp
+++ b/widget/src/gtk2/nsNativeKeyBindings.cpp
@@ -275,45 +275,74 @@ nsNativeKeyBindings::KeyPress(const nsNa
 {
   PRUint32 keyCode;
 
   if (aEvent.charCode != 0)
     keyCode = gdk_unicode_to_keyval(aEvent.charCode);
   else
     keyCode = DOMKeyCodeToGdkKeyCode(aEvent.keyCode);
 
+  if (KeyPressInternal(aEvent, aCallback, aCallbackData, keyCode))
+    return PR_TRUE;
+
+  nsKeyEvent *nativeKeyEvent = static_cast<nsKeyEvent*>(aEvent.nativeEvent);
+  if (!nativeKeyEvent || nativeKeyEvent->eventStructType != NS_KEY_EVENT &&
+      nativeKeyEvent->message != NS_KEY_PRESS)
+    return PR_FALSE;
+
+  for (PRUint32 i = 0; i < nativeKeyEvent->alternativeCharCodes.Length(); ++i) {
+    PRUint32 ch = nativeKeyEvent->isShift ?
+        nativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode :
+        nativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode;
+    if (ch && ch != aEvent.charCode) {
+      keyCode = gdk_unicode_to_keyval(ch);
+      if (KeyPressInternal(aEvent, aCallback, aCallbackData, keyCode))
+        return PR_TRUE;
+    }
+  }
+
+/* gtk_bindings_activate_event is preferable, but it has unresolved bug: http://bugzilla.gnome.org/show_bug.cgi?id=162726
+Also gtk_bindings_activate may work with some non-shortcuts operations (todo: check it)
+See bugs 411005 406407
+
+  Code, which should be used after fixing http://bugzilla.gnome.org/show_bug.cgi?id=162726:
+  const nsGUIEvent *guiEvent = static_cast<nsGUIEvent*>(aEvent.nativeEvent);
+  if (guiEvent &&
+     (guiEvent->message == NS_KEY_PRESS || guiEvent->message == NS_KEY_UP || guiEvent->message == NS_KEY_DOWN) &&
+      guiEvent->nativeMsg)
+        gtk_bindings_activate_event(GTK_OBJECT(mNativeTarget),
+                                    static_cast<GdkEventKey*>(guiEvent->nativeMsg));
+*/
+
+  return PR_FALSE;
+}
+
+PRBool
+nsNativeKeyBindings::KeyPressInternal(const nsNativeKeyEvent& aEvent,
+                                      DoCommandCallback aCallback,
+                                      void *aCallbackData,
+                                      PRUint32 aKeyCode)
+{
   int modifiers = 0;
   if (aEvent.altKey)
     modifiers |= GDK_MOD1_MASK;
   if (aEvent.ctrlKey)
     modifiers |= GDK_CONTROL_MASK;
   if (aEvent.shiftKey)
     modifiers |= GDK_SHIFT_MASK;
   // we don't support meta
 
   gCurrentCallback = aCallback;
   gCurrentCallbackData = aCallbackData;
 
   gHandled = PR_FALSE;
 
   gtk_bindings_activate(GTK_OBJECT(mNativeTarget),
-                        keyCode, GdkModifierType(modifiers));
-
-/* gtk_bindings_activate_event is preferable, but it has unresolved bug: http://bugzilla.gnome.org/show_bug.cgi?id=162726
-Also gtk_bindings_activate may work with some non-shortcuts operations (todo: check it)
-See bugs 411005 406407
+                        aKeyCode, GdkModifierType(modifiers));
 
-  Code, which should be used after fixing http://bugzilla.gnome.org/show_bug.cgi?id=162726:
-  const nsGUIEvent *guiEvent = static_cast<nsGUIEvent*>(aEvent.nativeEvent);
-  if (guiEvent &&
-     (guiEvent->message == NS_KEY_PRESS || guiEvent->message == NS_KEY_UP || guiEvent->message == NS_KEY_DOWN) &&
-      guiEvent->nativeMsg)
-        gtk_bindings_activate_event(GTK_OBJECT(mNativeTarget),
-                                    static_cast<GdkEventKey*>(guiEvent->nativeMsg));
-*/
   gCurrentCallback = nsnull;
   gCurrentCallbackData = nsnull;
 
   return gHandled;
 }
 
 PRBool
 nsNativeKeyBindings::KeyUp(const nsNativeKeyEvent& aEvent,
--- a/widget/src/gtk2/nsNativeKeyBindings.h
+++ b/widget/src/gtk2/nsNativeKeyBindings.h
@@ -88,12 +88,17 @@ public:
 
   virtual NS_HIDDEN_(PRBool) KeyUp(const nsNativeKeyEvent& aEvent,
                                    DoCommandCallback aCallback,
                                    void *aCallbackData);
 
 private:
   ~nsNativeKeyBindings() NS_HIDDEN;
 
+  PRBool KeyPressInternal(const nsNativeKeyEvent& aEvent,
+                          DoCommandCallback aCallback,
+                          void *aCallbackData,
+                          PRUint32 aKeyCode);
+
   GtkWidget *mNativeTarget;
 };
 
 #endif
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -2271,33 +2271,64 @@ nsWindow::OnContainerFocusOutEvent(GtkWi
 
     gFocusWindow = nsnull;
 
     mActivatePending = PR_FALSE;
 
     LOGFOCUS(("Done with container focus out [%p]\n", (void *)this));
 }
 
-inline PRBool
-is_latin_shortcut_key(guint aKeyval)
-{
-    return ((GDK_0 <= aKeyval && aKeyval <= GDK_9) ||
-            (GDK_A <= aKeyval && aKeyval <= GDK_Z) ||
-            (GDK_a <= aKeyval && aKeyval <= GDK_z));
-}
-
 PRBool
 nsWindow::DispatchCommandEvent(nsIAtom* aCommand)
 {
     nsEventStatus status;
     nsCommandEvent event(PR_TRUE, nsWidgetAtoms::onAppCommand, aCommand, this);
     DispatchEvent(&event, status);
     return TRUE;
 }
 
+static PRUint32
+GetCharCodeFor(const GdkEventKey *aEvent, GdkModifierType aShiftState,
+               gint aGroup)
+{
+    guint keyval;
+    if (gdk_keymap_translate_keyboard_state(NULL,
+                                            aEvent->hardware_keycode,
+                                            aShiftState, aGroup,
+                                            &keyval, NULL, NULL, NULL)) {
+        GdkEventKey tmpEvent = *aEvent;
+        tmpEvent.state = guint(aShiftState);
+        tmpEvent.keyval = keyval;
+        tmpEvent.group = aGroup;
+        return nsConvertCharCodeToUnicode(&tmpEvent);
+    }
+    return 0;
+}
+
+static gint
+GetKeyLevel(GdkEventKey *aEvent)
+{
+    gint level;
+    if (!gdk_keymap_translate_keyboard_state(NULL,
+                                             aEvent->hardware_keycode,
+                                             GdkModifierType(aEvent->state),
+                                             aEvent->group,
+                                             NULL, NULL, &level, NULL))
+        return -1;
+    return level;
+}
+
+static PRBool
+IsBasicLatinLetterOrNumeral(PRUint32 aChar)
+{
+    return (aChar >= 'a' && aChar <= 'z') ||
+           (aChar >= 'A' && aChar <= 'Z') ||
+           (aChar >= '0' && aChar <= '9');
+}
+
 gboolean
 nsWindow::OnKeyPressEvent(GtkWidget *aWidget, GdkEventKey *aEvent)
 {
     LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this));
 
 #ifdef USE_XIM
     // if we are in the middle of composing text, XIM gets to see it
     // before mozilla does.
@@ -2377,101 +2408,70 @@ nsWindow::OnKeyPressEvent(GtkWidget *aWi
     InitKeyEvent(event, aEvent);
     if (isKeyDownCancelled) {
       // If prevent default set for onkeydown, do the same for onkeypress
       event.flags |= NS_EVENT_FLAG_NO_DEFAULT;
     }
     event.charCode = nsConvertCharCodeToUnicode(aEvent);
     if (event.charCode) {
         event.keyCode = 0;
-        // if the control, meta, or alt key is down, then we should leave
-        // the isShift flag alone (probably not a printable character)
-        // if none of the other modifier keys are pressed then we need to
-        // clear isShift so the character can be inserted in the editor
-
-        if (event.isControl || event.isAlt || event.isMeta) {
-            GdkEventKey tmpEvent = *aEvent;
-
-            // Fix for bug 69230:
-            // if modifier key is pressed and key pressed is not latin character,
-            // we should try other keyboard layouts to find out correct latin
-            // character corresponding to pressed key;
-            // that way shortcuts like Ctrl+C will work no matter what
-            // keyboard layout is selected
-            // We don't try to fix up punctuation accelerators here,
-            // because their location differs between latin layouts
-            if (!is_latin_shortcut_key(event.charCode)) {
-                // We have a non-latin char, try other keyboard groups
+        gint level = GetKeyLevel(aEvent);
+        if ((event.isControl || event.isAlt || event.isMeta) &&
+            (level == 0 || level == 1)) {
+            // We shold send both shifted char and unshifted char,
+            // all keyboard layout users can use all keys.
+            // Don't change event.charCode. On some keyboard layouts,
+            // ctrl/alt/meta keys are used for inputting some characters.
+            nsAlternativeCharCode altCharCodes(0, 0);
+            // unshifted charcode of current keyboard layout.
+            altCharCodes.mUnshiftedCharCode =
+                GetCharCodeFor(aEvent, GdkModifierType(0), aEvent->group);
+            PRBool isLatin = (altCharCodes.mUnshiftedCharCode <= 0xFF);
+            // shifted charcode of current keyboard layout.
+            altCharCodes.mShiftedCharCode =
+                GetCharCodeFor(aEvent, GDK_SHIFT_MASK, aEvent->group);
+            isLatin = isLatin && (altCharCodes.mShiftedCharCode <= 0xFF);
+            if (altCharCodes.mUnshiftedCharCode ||
+                altCharCodes.mShiftedCharCode) {
+                event.alternativeCharCodes.AppendElement(altCharCodes);
+            }
+
+            if (!isLatin) {
+                // Next, find latin inputtable keyboard layout.
                 GdkKeymapKey *keys;
-                guint *keyvals;
-                gint n_entries;
-                PRUint32 latinCharCode;
-                gint level;
-
-                if (gdk_keymap_translate_keyboard_state(NULL,
-                                                        tmpEvent.hardware_keycode,
-                                                        (GdkModifierType)tmpEvent.state,
-                                                        tmpEvent.group,
-                                                        NULL, NULL, &level, NULL)
-                    && gdk_keymap_get_entries_for_keycode(NULL,
-                                                          tmpEvent.hardware_keycode,
-                                                          &keys, &keyvals,
-                                                          &n_entries)) {
-                    gint n;
-                    for (n=0; n<n_entries; n++) {
-                        if (keys[n].group == tmpEvent.group) {
-                            // Skip keys from the same group
+                gint count;
+                gint minGroup = -1;
+                if (gdk_keymap_get_entries_for_keyval(NULL, GDK_a,
+                                                      &keys, &count)) {
+                    // find the minimum number group for latin inputtable layout
+                    for (gint i = 0; i < count && minGroup != 0; ++i) {
+                        if (keys[i].level != 0 && keys[i].level != 1)
                             continue;
-                        }
-                        if (keys[n].level != level) {
-                            // Allow only same level keys
+                        if (minGroup >= 0 && keys[i].group > minGroup)
                             continue;
-                        }
-                        if (is_latin_shortcut_key(keyvals[n])) {
-                            // Latin character found
-                            if (event.isShift)
-                                tmpEvent.keyval = gdk_keyval_to_upper(keyvals[n]);
-                            else
-                                tmpEvent.keyval = gdk_keyval_to_lower(keyvals[n]);
-                            tmpEvent.group = keys[n].group;
-                            latinCharCode = nsConvertCharCodeToUnicode(&tmpEvent);
-                            if (latinCharCode) {
-                                event.charCode = latinCharCode;
-                                break;
-                            }
-                        }
+                        minGroup = keys[i].group;
                     }
                     g_free(keys);
-                    g_free(keyvals);
+                }
+                if (minGroup >= 0) {
+                    // unshifted charcode of found keyboard layout.
+                    PRUint32 ch =
+                        GetCharCodeFor(aEvent, GdkModifierType(0), minGroup);
+                    altCharCodes.mUnshiftedCharCode =
+                        IsBasicLatinLetterOrNumeral(ch) ? ch : 0;
+                    // shifted charcode of found keyboard layout.
+                    ch = GetCharCodeFor(aEvent, GDK_SHIFT_MASK, minGroup);
+                    altCharCodes.mShiftedCharCode =
+                        IsBasicLatinLetterOrNumeral(ch) ? ch : 0;
+                    if (altCharCodes.mUnshiftedCharCode ||
+                        altCharCodes.mShiftedCharCode) {
+                        event.alternativeCharCodes.AppendElement(altCharCodes);
+                    }
                 }
             }
-
-           // make Ctrl+uppercase functional as same as Ctrl+lowercase
-           // when Ctrl+uppercase(eg.Ctrl+C) is pressed,convert the charCode
-           // from uppercase to lowercase(eg.Ctrl+c),so do Alt and Meta Key
-           // It is hack code for bug 61355, there is same code snip for
-           // Windows platform in widget/src/windows/nsWindow.cpp: See bug 16486
-           // Note: if Shift is pressed at the same time, do not to_lower()
-           // Because Ctrl+Shift has different function with Ctrl
-           if (!event.isShift &&
-               event.charCode >= GDK_A &&
-               event.charCode <= GDK_Z)
-            event.charCode = gdk_keyval_to_lower(event.charCode);
-
-           // Keep the characters unshifted for shortcuts and accesskeys and
-           // make sure that numbers are always passed as such (among others:
-           // bugs 50255 and 351310)
-           if (!event.isControl && event.isShift &&
-               (event.charCode < GDK_0 || event.charCode > GDK_9)) {
-               GdkKeymapKey k = { tmpEvent.hardware_keycode, tmpEvent.group, 0 };
-               tmpEvent.keyval = gdk_keymap_lookup_key(gdk_keymap_get_default(), &k);
-               PRUint32 unshiftedCharCode = nsConvertCharCodeToUnicode(&tmpEvent);
-               if (unshiftedCharCode)
-                   event.charCode = unshiftedCharCode;
-           }
         }
     }
 
     // before we dispatch a key, check if it's the context menu key.
     // If so, send a context menu key event instead.
     if (is_context_menu_key(event)) {
         nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, this,
                                       nsMouseEvent::eReal,
--- a/widget/src/windows/nsKeyboardLayout.cpp
+++ b/widget/src/windows/nsKeyboardLayout.cpp
@@ -319,16 +319,40 @@ PRUint32 KeyboardLayout::GetUniChars (PR
   memcpy (aShiftStates, mShiftStates, chars);
 
   return chars;
 #else
   return 0;
 #endif
 }
 
+PRUint32
+KeyboardLayout::GetUniCharsWithShiftState(PRUint8 aVirtualKey,
+                                          PRUint8 aShiftStates,
+                                          PRUint16* aUniChars,
+                                          PRUint32 aMaxChars) const
+{
+#ifndef WINCE
+  PRInt32 key = GetKeyIndex(aVirtualKey);
+  if (key < 0)
+    return 0;
+  PRUint8 finalShiftState;
+  PRUint16 uniChars[5];
+  PRUint32 numOfBaseChars =
+    mVirtualKeys[key].GetUniChars(aShiftStates, uniChars, &finalShiftState);
+  PRUint32 chars = PR_MIN(numOfBaseChars, aMaxChars);
+
+  memcpy(aUniChars, uniChars, chars * sizeof (PRUint16));
+
+  return chars;
+#else
+  return 0;
+#endif
+}
+
 void KeyboardLayout::LoadLayout ()
 {
 #ifndef WINCE
   PRUint32 shiftState;
   BYTE kbdState [256];
   BYTE originalKbdState [256];
   PRUint16 shiftStatesWithDeadKeys = 0;     // Bitfield with all shift states that have at least one dead-key.
   PRUint16 shiftStatesWithBaseChars = 0;    // Bitfield with all shift states that produce any possible dead-key base characters.
--- a/widget/src/windows/nsKeyboardLayout.h
+++ b/widget/src/windows/nsKeyboardLayout.h
@@ -175,11 +175,14 @@ public:
 #else
     return PR_FALSE;
 #endif
   }
 
   void LoadLayout ();
   void OnKeyDown (PRUint8 aVirtualKey);
   PRUint32 GetUniChars (PRUint16* aUniChars, PRUint8* aShiftStates, PRUint32 aMaxChars) const;
+  PRUint32 GetUniCharsWithShiftState(PRUint8 aVirtualKey, PRUint8 aShiftStates,
+                                     PRUint16* aUniChars,
+                                     PRUint32 aMaxChars) const;
 };
 
 #endif
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -3035,26 +3035,33 @@ UINT nsWindow::MapFromNativeToDOM(UINT a
   return aNativeKeyCode;
 }
 
 //-------------------------------------------------------------------------
 //
 // OnKey
 //
 //-------------------------------------------------------------------------
-PRBool nsWindow::DispatchKeyEvent(PRUint32 aEventType, WORD aCharCode, UINT aVirtualCharCode, 
+PRBool nsWindow::DispatchKeyEvent(PRUint32 aEventType, WORD aCharCode,
+                                  PRUint32 aUnshiftedCharCode,
+                                  PRUint32 aShiftedCharCode,
+                                  UINT aVirtualCharCode,
                                   LPARAM aKeyData, PRUint32 aFlags)
 {
   nsKeyEvent event(PR_TRUE, aEventType, this);
   nsPoint point(0, 0);
 
   InitEvent(event, &point); // this add ref's event.widget
 
   event.flags |= aFlags;
   event.charCode = aCharCode;
+  if (aUnshiftedCharCode || aShiftedCharCode) {
+    nsAlternativeCharCode altCharCodes(aUnshiftedCharCode, aShiftedCharCode);
+    event.alternativeCharCodes.AppendElement(altCharCodes);
+  }
   event.keyCode  = aVirtualCharCode;
 
 #ifdef KE_DEBUG
   static cnt=0;
   printf("%d DispatchKE Type: %s charCode %d  keyCode %d ", cnt++,
         (NS_KEY_PRESS == aEventType) ? "PRESS" : (aEventType == NS_KEY_UP ? "Up" : "Down"),
          event.charCode, event.keyCode);
   printf("Shift: %s Control %s Alt: %s \n", 
@@ -3128,17 +3135,17 @@ BOOL nsWindow::OnKeyDown(UINT aVirtualKe
   // Use aVirtualKeyCode for gKbdLayout and native processing.
   UINT DOMKeyCode = sIMEIsComposing ?
                       aVirtualKeyCode : MapFromNativeToDOM(aVirtualKeyCode);
 
 #ifdef DEBUG
   //printf("In OnKeyDown virt: %d  scan: %d\n", DOMKeyCode, aScanCode);
 #endif
 
-  BOOL noDefault = DispatchKeyEvent(NS_KEY_DOWN, 0, DOMKeyCode, aKeyData);
+  BOOL noDefault = DispatchKeyEvent(NS_KEY_DOWN, 0, 0, 0, DOMKeyCode, aKeyData);
 
   // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a keypress
   // for almost all keys
   switch (DOMKeyCode) {
     case NS_VK_SHIFT:
     case NS_VK_CONTROL:
     case NS_VK_ALT:
     case NS_VK_CAPS_LOCK:
@@ -3228,19 +3235,23 @@ BOOL nsWindow::OnKeyDown(UINT aVirtualKe
     // this event may not input text, so we should ignore this event.
     // See bug 314130.
     return PR_FALSE;
   }
 
   if (gKbdLayout.IsDeadKey ())
     return PR_FALSE;
 
-  PRUint8 shiftStates [5];
-  PRUint16 uniChars [5];
+  PRUint8 shiftStates[5];
+  PRUint16 uniChars[5];
+  PRUint16 shiftedChars[5] = {0, 0, 0, 0, 0};
+  PRUint16 unshiftedChars[5] = {0, 0, 0, 0, 0};
   PRUint32 numOfUniChars = 0;
+  PRUint32 numOfShiftedChars = 0;
+  PRUint32 numOfUnshiftedChars = 0;
   PRUint32 numOfShiftStates = 0;
 
   switch (aVirtualKeyCode) {
     // keys to be sent as characters
     case VK_ADD:       uniChars [0] = '+';  numOfUniChars = 1;  break;
     case VK_SUBTRACT:  uniChars [0] = '-';  numOfUniChars = 1;  break;
     case VK_DIVIDE:    uniChars [0] = '/';  numOfUniChars = 1;  break;
     case VK_MULTIPLY:  uniChars [0] = '*';  numOfUniChars = 1;  break;
@@ -3253,94 +3264,82 @@ BOOL nsWindow::OnKeyDown(UINT aVirtualKe
     case VK_NUMPAD6:
     case VK_NUMPAD7:
     case VK_NUMPAD8:
     case VK_NUMPAD9:
       uniChars [0] = aVirtualKeyCode - VK_NUMPAD0 + '0';
       numOfUniChars = 1;
       break;
     default:
-      if (KeyboardLayout::IsPrintableCharKey (aVirtualKeyCode))
-        numOfUniChars = numOfShiftStates = gKbdLayout.GetUniChars (uniChars, shiftStates, NS_ARRAY_LENGTH (uniChars));
-
-      if (mIsControlDown ^ mIsAltDown)
-      {
-        // XXX
-        // For both Alt+key and Ctrl+key combinations we return the latin characters A..Z and
-        // numbers 0..9, ignoring the real characters returned by active keyboard layout.
-        // This is required to make sure that all shortcut keys (e.g. Ctrl+c, Ctrl+1, Alt+f)
-        // work the same way no matter what keyboard layout you are using.
-
-        if ((NS_VK_0 <= DOMKeyCode && DOMKeyCode <= NS_VK_9) ||
-            (NS_VK_A <= DOMKeyCode && DOMKeyCode <= NS_VK_Z))
-        {
-          uniChars [0] = DOMKeyCode;
-          numOfUniChars = 1;
-          numOfShiftStates = 0;
-
-          // For letters take the Shift state into account
-          if (!mIsShiftDown &&
-              NS_VK_A <= DOMKeyCode && DOMKeyCode <= NS_VK_Z)
-            uniChars [0] += 0x20;
+      if (KeyboardLayout::IsPrintableCharKey(aVirtualKeyCode)) {
+        numOfUniChars = numOfShiftStates =
+          gKbdLayout.GetUniChars(uniChars, shiftStates,
+                                 NS_ARRAY_LENGTH(uniChars));
+      }
+
+      if (mIsControlDown ^ mIsAltDown) {
+        numOfUnshiftedChars =
+          gKbdLayout.GetUniCharsWithShiftState(aVirtualKeyCode, 0,
+                       unshiftedChars, NS_ARRAY_LENGTH(unshiftedChars));
+        numOfShiftedChars =
+          gKbdLayout.GetUniCharsWithShiftState(aVirtualKeyCode, eShift,
+                       shiftedChars, NS_ARRAY_LENGTH(shiftedChars));
+      }
+  }
+
+  if (numOfUniChars > 0 || numOfShiftedChars > 0 || numOfUnshiftedChars > 0) {
+    PRUint32 num = PR_MAX(numOfUniChars,
+                          PR_MAX(numOfShiftedChars, numOfUnshiftedChars));
+    PRUint32 skipUniChars = num - numOfUniChars;
+    PRUint32 skipShiftedChars = num - numOfShiftedChars;
+    PRUint32 skipUnshiftedChars = num - numOfUnshiftedChars;
+    UINT keyCode = numOfUniChars == 0 ? DOMKeyCode : 0;
+    for (PRUint32 cnt = 0; cnt < num; cnt++) {
+      PRUint16 uniChar, shiftedChar, unshiftedChar;
+      uniChar = shiftedChar = unshiftedChar = 0;
+      if (skipUniChars <= cnt) {
+        if (cnt - skipUniChars  < numOfShiftStates) {
+          // If key in combination with Alt and/or Ctrl produces a different
+          // character than without them then do not report these flags
+          // because it is separate keyboard layout shift state. If dead-key
+          // and base character does not produce a valid composite character
+          // then both produced dead-key character and following base
+          // character may have different modifier flags, too.
+          mIsShiftDown   = (shiftStates[cnt - skipUniChars] & eShift) != 0;
+          mIsControlDown = (shiftStates[cnt - skipUniChars] & eCtrl) != 0;
+          mIsAltDown     = (shiftStates[cnt - skipUniChars] & eAlt) != 0;
         }
-        else if (!anyCharMessagesRemoved && DOMKeyCode != aVirtualKeyCode) {
-          switch (DOMKeyCode) {
-            case NS_VK_ADD:
-              uniChars [0] = '+'; numOfUniChars = 1; break;
-            case NS_VK_SUBTRACT:
-              uniChars [0] = '-'; numOfUniChars = 1; break;
-            case NS_VK_SEMICOLON:
-              // XXXmnakano I don't know whether this is correct.
-              uniChars [0] = ';';
-              uniChars [1] = ':';
-              numOfUniChars = 2;
-              break;
-            default:
-              NS_ERROR("implement me!");
-          }
-        }
+        uniChar = uniChars[cnt - skipUniChars];
       }
-  }
-
-  if (numOfUniChars)
-  {
-    for (PRUint32 cnt = 0; cnt < numOfUniChars; cnt++)
-    {
-      if (cnt < numOfShiftStates)
-      {
-        // If key in combination with Alt and/or Ctrl produces a different character than without them
-        // then do not report these flags because it is separate keyboard layout shift state.
-        // If dead-key and base character does not produce a valid composite character then both produced
-        // dead-key character and following base character may have different modifier flags, too.
-        mIsShiftDown   = (shiftStates [cnt] & eShift) != 0;
-        mIsControlDown = (shiftStates [cnt] & eCtrl) != 0;
-        mIsAltDown     = (shiftStates [cnt] & eAlt) != 0;
-      }
-
-      DispatchKeyEvent(NS_KEY_PRESS, uniChars [cnt], 0, aKeyData, extraFlags);
+      if (skipShiftedChars <= cnt)
+        shiftedChar = shiftedChars[cnt - skipShiftedChars];
+      if (skipUnshiftedChars <= cnt)
+        unshiftedChar = unshiftedChars[cnt - skipUnshiftedChars];
+      DispatchKeyEvent(NS_KEY_PRESS, uniChar, unshiftedChar,
+                       shiftedChar, keyCode, aKeyData, extraFlags);
     }
   } else
-    DispatchKeyEvent(NS_KEY_PRESS, 0, DOMKeyCode, aKeyData, extraFlags);
+    DispatchKeyEvent(NS_KEY_PRESS, 0, 0, 0, DOMKeyCode, aKeyData, extraFlags);
 
   return noDefault;
 }
 
 //-------------------------------------------------------------------------
 //
 //
 //-------------------------------------------------------------------------
 BOOL nsWindow::OnKeyUp( UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyData)
 {
 #ifdef VK_BROWSER_BACK
   if (aVirtualKeyCode == VK_BROWSER_BACK || aVirtualKeyCode == VK_BROWSER_FORWARD) 
     return TRUE;
 #endif
 
   aVirtualKeyCode = sIMEIsComposing ? aVirtualKeyCode : MapFromNativeToDOM(aVirtualKeyCode);
-  BOOL result = DispatchKeyEvent(NS_KEY_UP, 0, aVirtualKeyCode, aKeyData);
+  BOOL result = DispatchKeyEvent(NS_KEY_UP, 0, 0, 0, aVirtualKeyCode, aKeyData);
   return result;
 }
 
 
 //-------------------------------------------------------------------------
 //
 //
 //-------------------------------------------------------------------------
@@ -3407,17 +3406,18 @@ BOOL nsWindow::OnChar(UINT charCode, LPA
 
   // Fix for bug 285161 (and 295095) which was caused by the initial fix for bug 178110.
   // When pressing (alt|ctrl)+char, the char must be lowercase unless shift is 
   // pressed too.
   if (!mIsShiftDown && (saveIsAltDown || saveIsControlDown)) {
     uniChar = towlower(uniChar);
   }
 
-  PRBool result = DispatchKeyEvent(NS_KEY_PRESS, uniChar, charCode, 0, aFlags);
+  PRBool result = DispatchKeyEvent(NS_KEY_PRESS, uniChar, 0, 0,
+                                   charCode, 0, aFlags);
   mIsAltDown = saveIsAltDown;
   mIsControlDown = saveIsControlDown;
   return result;
 }
 
 
 void nsWindow::ConstrainZLevel(HWND *aAfter)
 {
@@ -6530,17 +6530,17 @@ BOOL nsWindow::OnIMEChar(BYTE aByte1, BY
         printf("ERROR_NO_UNICODE_TRANSLATION\n");
         break;
     }
   }
 #endif
 
   // We need to return TRUE here so that Windows doesn't
   // send two WM_CHAR msgs
-  DispatchKeyEvent(NS_KEY_PRESS, uniChar, 0, 0);
+  DispatchKeyEvent(NS_KEY_PRESS, uniChar, 0, 0, 0, 0);
   return PR_TRUE;
 }
 
 //==========================================================================
 // This function is used when aIndex is GCS_COMPSTR, GCS_COMPREADSTR,
 // GCS_RESULTSTR, and GCS_RESULTREADSTR.
 // Otherwise use ::ImmGetCompositionStringW.
 void nsWindow::GetCompositionString(HIMC aHIMC, DWORD aIndex, nsString* aStrUnicode)
@@ -6810,17 +6810,17 @@ BOOL nsWindow::OnIMENotify(WPARAM aIMN, 
 #endif
 
   // add hacky code here
   if (IS_VK_DOWN(NS_VK_ALT)) {
     mIsShiftDown = PR_FALSE;
     mIsControlDown = PR_FALSE;
     mIsAltDown = PR_TRUE;
 
-    DispatchKeyEvent(NS_KEY_PRESS, 0, 192, 0); // XXX hack hack hack
+    DispatchKeyEvent(NS_KEY_PRESS, 0, 0, 0, 192, 0); // XXX hack hack hack
     if (aIMN == IMN_SETOPENSTATUS)
       sIMEIsStatusChanged = PR_TRUE;
   }
   // not implemented yet
   return PR_FALSE;
 }
 //==========================================================================
 BOOL nsWindow::OnIMERequest(WPARAM aIMR, LPARAM aData, LRESULT *oResult)
--- a/widget/src/windows/nsWindow.h
+++ b/widget/src/windows/nsWindow.h
@@ -326,18 +326,22 @@ protected:
   BOOL                    OnIMEReconvert(LPARAM aData, LRESULT *oResult);
   BOOL                    OnIMEQueryCharPosition(LPARAM aData, LRESULT *oResult);
 
   void                    GetCompositionString(HIMC aHIMC, DWORD aIndex, nsString* aStrUnicode);
   void                    ResolveIMECaretPos(nsWindow* aClient,
                                              nsRect&   aEventResult,
                                              nsRect&   aResult);
 
-  virtual PRBool          DispatchKeyEvent(PRUint32 aEventType, WORD aCharCode, UINT aVirtualCharCode,
-                                           LPARAM aKeyCode, PRUint32 aFlags = 0);
+  virtual PRBool          DispatchKeyEvent(PRUint32 aEventType, WORD aCharCode,
+                                           PRUint32 aUnshiftedCharCode,
+                                           PRUint32 aShiftedCharCodes,
+                                           UINT aVirtualCharCode,
+                                           LPARAM aKeyCode,
+                                           PRUint32 aFlags = 0);
 
   virtual PRBool          DispatchFocus(PRUint32 aEventType, PRBool isMozWindowTakingFocus);
   virtual PRBool          OnScroll(UINT scrollCode, int cPos);
   virtual HBRUSH          OnControlColor();
 
   static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
   static LRESULT CALLBACK DefaultWindowProc(HWND hWns, UINT msg, WPARAM wParam, LPARAM lParam);