Bug 1154183 part.1 Move shortcut/access key candidate list creators from nsContentUtils to WidgetKeyboardEvent r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 18 Mar 2016 11:22:37 +0900
changeset 327818 6e01f6bf3b982735e4c51ce0ed60862091648145
parent 327817 dd12327c3e0c9f21326236ab8843f6f934579490
child 327819 b6bf385a7c2a84e72ce0caab657c082625381045
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1154183
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1154183 part.1 Move shortcut/access key candidate list creators from nsContentUtils to WidgetKeyboardEvent r=smaug MozReview-Commit-ID: Ied6qEUc2Kz
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/events/EventStateManager.cpp
dom/xbl/nsXBLEventHandler.cpp
dom/xbl/nsXBLWindowKeyHandler.cpp
layout/xul/nsMenuBarFrame.cpp
layout/xul/nsMenuBarListener.cpp
widget/EventForwards.h
widget/TextEvents.h
widget/WidgetEventImpl.cpp
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -4900,198 +4900,16 @@ nsContentUtils::GetLocalizedEllipsis()
                           uint32_t(ArrayLength(sBuf) - 1));
     CopyUnicodeTo(tmp, 0, sBuf, len);
     if (!sBuf[0])
       sBuf[0] = char16_t(0x2026);
   }
   return nsDependentString(sBuf);
 }
 
-static bool
-HasASCIIDigit(const nsTArray<nsShortcutCandidate>& aCandidates)
-{
-  for (uint32_t i = 0; i < aCandidates.Length(); ++i) {
-    uint32_t ch = aCandidates[i].mCharCode;
-    if (ch >= '0' && ch <= '9')
-      return true;
-  }
-  return false;
-}
-
-static bool
-CharsCaseInsensitiveEqual(uint32_t aChar1, uint32_t aChar2)
-{
-  return aChar1 == aChar2 ||
-         (IS_IN_BMP(aChar1) && IS_IN_BMP(aChar2) &&
-          ToLowerCase(char16_t(aChar1)) == ToLowerCase(char16_t(aChar2)));
-}
-
-static bool
-IsCaseChangeableChar(uint32_t aChar)
-{
-  return IS_IN_BMP(aChar) &&
-         ToLowerCase(char16_t(aChar)) != ToUpperCase(char16_t(aChar));
-}
-
-/* static */
-void
-nsContentUtils::GetAccelKeyCandidates(nsIDOMKeyEvent* aDOMKeyEvent,
-                  nsTArray<nsShortcutCandidate>& aCandidates)
-{
-  NS_PRECONDITION(aCandidates.IsEmpty(), "aCandidates must be empty");
-
-  nsAutoString eventType;
-  aDOMKeyEvent->AsEvent()->GetType(eventType);
-  // Don't process if aDOMKeyEvent is not a keypress event.
-  if (!eventType.EqualsLiteral("keypress"))
-    return;
-
-  WidgetKeyboardEvent* nativeKeyEvent =
-    aDOMKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
-  if (nativeKeyEvent) {
-    NS_ASSERTION(nativeKeyEvent->mClass == eKeyboardEventClass,
-                 "wrong type of native event");
-    // 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/false,
-    //   1: unshiftedCharCodes[0]/false, 2: unshiftedCharCodes[1]/false...
-    // the priority of the charCodes are (shift key is pressed):
-    //   0: charCode/false,
-    //   1: shiftedCharCodes[0]/false, 2: shiftedCharCodes[0]/true,
-    //   3: shiftedCharCodes[1]/false, 4: shiftedCharCodes[1]/true...
-    if (nativeKeyEvent->charCode) {
-      nsShortcutCandidate key(nativeKeyEvent->charCode, false);
-      aCandidates.AppendElement(key);
-    }
-
-    uint32_t len = nativeKeyEvent->alternativeCharCodes.Length();
-    if (!nativeKeyEvent->IsShift()) {
-      for (uint32_t i = 0; i < len; ++i) {
-        uint32_t ch =
-          nativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode;
-        if (!ch || ch == nativeKeyEvent->charCode)
-          continue;
-
-        nsShortcutCandidate key(ch, false);
-        aCandidates.AppendElement(key);
-      }
-      // If unshiftedCharCodes doesn't have numeric but shiftedCharCode has it,
-      // this keyboard layout is AZERTY or similar layout, probably.
-      // In this case, Accel+[0-9] should be accessible without shift key.
-      // However, the priority should be lowest.
-      if (!HasASCIIDigit(aCandidates)) {
-        for (uint32_t i = 0; i < len; ++i) {
-          uint32_t ch =
-            nativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode;
-          if (ch >= '0' && ch <= '9') {
-            nsShortcutCandidate key(ch, false);
-            aCandidates.AppendElement(key);
-            break;
-          }
-        }
-      }
-    } else {
-      for (uint32_t i = 0; i < len; ++i) {
-        uint32_t ch = nativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode;
-        if (!ch)
-          continue;
-
-        if (ch != nativeKeyEvent->charCode) {
-          nsShortcutCandidate key(ch, 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.
-        uint32_t unshiftCh =
-          nativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode;
-        if (CharsCaseInsensitiveEqual(ch, unshiftCh))
-          continue;
-
-        // On the Hebrew keyboard layout on Windows, the unshifted char is a
-        // localized character but the shifted char is a Latin alphabet,
-        // then, we should not execute without the shift state. See bug 433192.
-        if (IsCaseChangeableChar(ch))
-          continue;
-
-        // Setting the alternative charCode candidates for retry without shift
-        // key state only when the shift key is pressed.
-        nsShortcutCandidate key(ch, true);
-        aCandidates.AppendElement(key);
-      }
-    }
-
-    // Special case for "Space" key.  With some keyboard layouts, "Space" with
-    // or without Shift key causes non-ASCII space.  For such keyboard layouts,
-    // we should guarantee that the key press works as an ASCII white space key
-    // press.
-    if (nativeKeyEvent->mCodeNameIndex == CODE_NAME_INDEX_Space &&
-        nativeKeyEvent->charCode != static_cast<uint32_t>(' ')) {
-      nsShortcutCandidate spaceKey(static_cast<uint32_t>(' '), false);
-      aCandidates.AppendElement(spaceKey);
-    }
-  } else {
-    uint32_t charCode;
-    aDOMKeyEvent->GetCharCode(&charCode);
-    if (charCode) {
-      nsShortcutCandidate key(charCode, false);
-      aCandidates.AppendElement(key);
-    }
-  }
-}
-
-/* static */
-void
-nsContentUtils::GetAccessKeyCandidates(WidgetKeyboardEvent* aNativeKeyEvent,
-                                       nsTArray<uint32_t>& 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) {
-    uint32_t ch = aNativeKeyEvent->charCode;
-    if (IS_IN_BMP(ch))
-      ch = ToLowerCase(char16_t(ch));
-    aCandidates.AppendElement(ch);
-  }
-  for (uint32_t i = 0;
-       i < aNativeKeyEvent->alternativeCharCodes.Length(); ++i) {
-    uint32_t ch[2] =
-      { aNativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode,
-        aNativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode };
-    for (uint32_t j = 0; j < 2; ++j) {
-      if (!ch[j])
-        continue;
-      if (IS_IN_BMP(ch[j]))
-        ch[j] = ToLowerCase(char16_t(ch[j]));
-      // Don't append the charCode that was already appended.
-      if (aCandidates.IndexOf(ch[j]) == aCandidates.NoIndex)
-        aCandidates.AppendElement(ch[j]);
-    }
-  }
-  // Special case for "Space" key.  With some keyboard layouts, "Space" with
-  // or without Shift key causes non-ASCII space.  For such keyboard layouts,
-  // we should guarantee that the key press works as an ASCII white space key
-  // press.
-  if (aNativeKeyEvent->mCodeNameIndex == CODE_NAME_INDEX_Space &&
-      aNativeKeyEvent->charCode != static_cast<uint32_t>(' ')) {
-    aCandidates.AppendElement(static_cast<uint32_t>(' '));
-  }
-  return;
-}
-
 /* static */
 void
 nsContentUtils::AddScriptBlocker()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!sScriptBlockerCount) {
     MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
                "Should not already have a count");
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -166,25 +166,16 @@ struct EventNameMapping
   // This holds pointers to nsGkAtoms members, and is therefore safe as a
   // non-owning reference.
   nsIAtom* MOZ_NON_OWNING_REF mAtom;
   int32_t  mType;
   mozilla::EventMessage mMessage;
   mozilla::EventClassID mEventClassID;
 };
 
-struct nsShortcutCandidate {
-  nsShortcutCandidate(uint32_t aCharCode, bool aIgnoreShift) :
-    mCharCode(aCharCode), mIgnoreShift(aIgnoreShift)
-  {
-  }
-  uint32_t mCharCode;
-  bool     mIgnoreShift;
-};
-
 typedef void (*CallOnRemoteChildFunction) (mozilla::dom::TabParent* aTabParent,
                                            void* aArg);
 
 class nsContentUtils
 {
   friend class nsAutoScriptBlockerSuppressNodeRemoved;
   typedef mozilla::dom::Element Element;
   typedef mozilla::TimeDuration TimeDuration;
@@ -1507,37 +1498,16 @@ public:
   static nsIWidget* GetTopLevelWidget(nsIWidget* aWidget);
 
   /**
    * Return the localized ellipsis for UI.
    */
   static const nsDependentString GetLocalizedEllipsis();
 
   /**
-   * Get the candidates for accelkeys for aDOMKeyEvent.
-   *
-   * @param aDOMKeyEvent [in] the key event for accelkey handling.
-   * @param aCandidates [out] the candidate shortcut key combination list.
-   *                          the first item is most preferred.
-   */
-  static void GetAccelKeyCandidates(nsIDOMKeyEvent* aDOMKeyEvent,
-                                    nsTArray<nsShortcutCandidate>& aCandidates);
-
-  /**
-   * Get the candidates for accesskeys for aNativeKeyEvent.
-   *
-   * @param aNativeKeyEvent [in] the key event for accesskey handling.
-   * @param aCandidates [out] the candidate access key list.
-   *                          the first item is most preferred.
-   */
-  static void GetAccessKeyCandidates(
-                mozilla::WidgetKeyboardEvent* aNativeKeyEvent,
-                nsTArray<uint32_t>& aCandidates);
-
-  /**
    * Hide any XUL popups associated with aDocument, including any documents
    * displayed in child frames. Does nothing if aDocument is null.
    */
   static void HidePopupsInDocument(nsIDocument* aDocument);
 
   /**
    * Retrieve the current drag session, or null if no drag is currently occuring
    */
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -728,17 +728,17 @@ EventStateManager::PreHandleEvent(nsPres
       if (keyEvent->IsOS())
         modifierMask |= NS_MODIFIER_OS;
 
       // Prevent keyboard scrolling while an accesskey modifier is in use.
       if (modifierMask &&
           (modifierMask == Prefs::ChromeAccessModifierMask() ||
            modifierMask == Prefs::ContentAccessModifierMask())) {
         AutoTArray<uint32_t, 10> accessCharCodes;
-        nsContentUtils::GetAccessKeyCandidates(keyEvent, accessCharCodes);
+        keyEvent->GetAccessKeyCandidates(accessCharCodes);
 
         if (HandleAccessKey(aPresContext, accessCharCodes,
                             keyEvent->mFlags.mIsTrusted, modifierMask)) {
           *aStatus = nsEventStatus_eConsumeNoDefault;
         }
       }
     }
     // then fall through...
--- a/dom/xbl/nsXBLEventHandler.cpp
+++ b/dom/xbl/nsXBLEventHandler.cpp
@@ -135,28 +135,31 @@ nsXBLKeyEventHandler::HandleEvent(nsIDOM
     if (eventPhase != nsIDOMEvent::AT_TARGET)
       return NS_OK;
   }
 
   nsCOMPtr<nsIDOMKeyEvent> key(do_QueryInterface(aEvent));
   if (!key)
     return NS_OK;
 
-  AutoTArray<nsShortcutCandidate, 10> accessKeys;
-  nsContentUtils::GetAccelKeyCandidates(key, accessKeys);
+  WidgetKeyboardEvent* nativeKeyboardEvent =
+    aEvent->WidgetEventPtr()->AsKeyboardEvent();
+  MOZ_ASSERT(nativeKeyboardEvent);
+  AutoShortcutKeyCandidateArray shortcutKeys;
+  nativeKeyboardEvent->GetShortcutKeyCandidates(shortcutKeys);
 
-  if (accessKeys.IsEmpty()) {
+  if (shortcutKeys.IsEmpty()) {
     ExecuteMatchedHandlers(key, 0, IgnoreModifierState());
     return NS_OK;
   }
 
-  for (uint32_t i = 0; i < accessKeys.Length(); ++i) {
+  for (uint32_t i = 0; i < shortcutKeys.Length(); ++i) {
     IgnoreModifierState ignoreModifierState;
-    ignoreModifierState.mShift = accessKeys[i].mIgnoreShift;
-    if (ExecuteMatchedHandlers(key, accessKeys[i].mCharCode,
+    ignoreModifierState.mShift = shortcutKeys[i].mIgnoreShift;
+    if (ExecuteMatchedHandlers(key, shortcutKeys[i].mCharCode,
                                ignoreModifierState)) {
       return NS_OK;
     }
   }
   return NS_OK;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////
--- a/dom/xbl/nsXBLWindowKeyHandler.cpp
+++ b/dom/xbl/nsXBLWindowKeyHandler.cpp
@@ -538,27 +538,31 @@ nsXBLWindowKeyHandler::IsHTMLEditableFie
 //
 bool
 nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
                                             nsIAtom* aEventType, 
                                             nsXBLPrototypeHandler* aHandler,
                                             bool aExecute,
                                             bool* aOutReservedForChrome)
 {
-  AutoTArray<nsShortcutCandidate, 10> accessKeys;
-  nsContentUtils::GetAccelKeyCandidates(aKeyEvent, accessKeys);
+  WidgetKeyboardEvent* nativeKeyboardEvent =
+    aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
+  MOZ_ASSERT(nativeKeyboardEvent);
 
-  if (accessKeys.IsEmpty()) {
+  AutoShortcutKeyCandidateArray shortcutKeys;
+  nativeKeyboardEvent->GetShortcutKeyCandidates(shortcutKeys);
+
+  if (shortcutKeys.IsEmpty()) {
     return WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
                                   0, IgnoreModifierState(),
                                   aExecute, aOutReservedForChrome);
   }
 
-  for (uint32_t i = 0; i < accessKeys.Length(); ++i) {
-    nsShortcutCandidate &key = accessKeys[i];
+  for (uint32_t i = 0; i < shortcutKeys.Length(); ++i) {
+    ShortcutKeyCandidate& key = shortcutKeys[i];
     IgnoreModifierState ignoreModifierState;
     ignoreModifierState.mShift = key.mIgnoreShift;
     if (WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
                                key.mCharCode, ignoreModifierState,
                                aExecute, aOutReservedForChrome)) {
       return true;
     }
   }
--- a/layout/xul/nsMenuBarFrame.cpp
+++ b/layout/xul/nsMenuBarFrame.cpp
@@ -162,18 +162,19 @@ nsMenuFrame*
 nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
 {
   uint32_t charCode;
   aKeyEvent->GetCharCode(&charCode);
 
   AutoTArray<uint32_t, 10> accessKeys;
   WidgetKeyboardEvent* nativeKeyEvent =
     aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
-  if (nativeKeyEvent)
-    nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, accessKeys);
+  if (nativeKeyEvent) {
+    nativeKeyEvent->GetAccessKeyCandidates(accessKeys);
+  }
   if (accessKeys.IsEmpty() && charCode)
     accessKeys.AppendElement(charCode);
 
   if (accessKeys.IsEmpty())
     return nullptr; // no character was pressed so just return
 
   // Enumerate over our list of frames.
   auto insertion = PresContext()->PresShell()->FrameConstructor()->
--- a/layout/xul/nsMenuBarListener.cpp
+++ b/layout/xul/nsMenuBarListener.cpp
@@ -217,17 +217,17 @@ nsMenuBarListener::KeyPress(nsIDOMEvent*
       keyEvent->GetCharCode(&charCode);
 
       bool hasAccessKeyCandidates = charCode != 0;
       if (!hasAccessKeyCandidates) {
         WidgetKeyboardEvent* nativeKeyEvent =
           aKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
         if (nativeKeyEvent) {
           AutoTArray<uint32_t, 10> keys;
-          nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, keys);
+          nativeKeyEvent->GetAccessKeyCandidates(keys);
           hasAccessKeyCandidates = !keys.IsEmpty();
         }
       }
 
       // Cancel the access key flag unless we are pressing the access key.
       if (keyCode != (uint32_t)mAccessKey) {
         mAccessKeyDownCanceled = true;
       }
--- a/widget/EventForwards.h
+++ b/widget/EventForwards.h
@@ -3,16 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_EventForwards_h__
 #define mozilla_EventForwards_h__
 
 #include <stdint.h>
 
+#include "nsTArray.h"
+
 /**
  * XXX Following enums should be in BasicEvents.h.  However, currently, it's
  *     impossible to use foward delearation for enum.
  */
 
 /**
  * Return status for event processors.
  */
@@ -134,16 +136,20 @@ namespace mozilla {
 // BasicEvents.h
 struct BaseEventFlags;
 struct EventFlags;
 
 class WidgetEventTime;
 
 // TextEvents.h
 struct AlternativeCharCode;
+struct ShortcutKeyCandidate;
+
+typedef nsTArray<ShortcutKeyCandidate> ShortcutKeyCandidateArray;
+typedef AutoTArray<ShortcutKeyCandidate, 10> AutoShortcutKeyCandidateArray;
 
 // TextRange.h
 struct TextRangeStyle;
 struct TextRange;
 
 class TextRangeArray;
 
 // FontRange.h
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -66,16 +66,36 @@ struct AlternativeCharCode
     mUnshiftedCharCode(aUnshiftedCharCode), mShiftedCharCode(aShiftedCharCode)
   {
   }
   uint32_t mUnshiftedCharCode;
   uint32_t mShiftedCharCode;
 };
 
 /******************************************************************************
+ * mozilla::ShortcutKeyCandidate
+ *
+ * This stores a candidate of shortcut key combination.
+ ******************************************************************************/
+
+struct ShortcutKeyCandidate
+{
+  ShortcutKeyCandidate(uint32_t aCharCode, bool aIgnoreShift)
+    : mCharCode(aCharCode)
+    , mIgnoreShift(aIgnoreShift)
+  {
+  }
+  // The charCode value which must match keyboard shortcut definition.
+  uint32_t mCharCode;
+  // true if Shift state can be ignored.  Otherwise, Shift key state must
+  // match keyboard shortcut definition.
+  bool mIgnoreShift;
+};
+
+/******************************************************************************
  * mozilla::WidgetKeyboardEvent
  ******************************************************************************/
 
 class WidgetKeyboardEvent : public WidgetInputEvent
 {
 private:
   friend class dom::PBrowserParent;
   friend class dom::PBrowserChild;
@@ -206,16 +226,32 @@ public:
     GetDOMCodeName(mCodeNameIndex, aCodeName);
   }
 
   bool IsModifierKeyEvent() const
   {
     return GetModifierForKeyName(mKeyNameIndex) != MODIFIER_NONE;
   }
 
+  /**
+   * Get the candidates for shortcut key.
+   *
+   * @param aCandidates [out] the candidate shortcut key combination list.
+   *                          the first item is most preferred.
+   */
+  void GetShortcutKeyCandidates(ShortcutKeyCandidateArray& aCandidates);
+
+  /**
+   * Get the candidates for access key.
+   *
+   * @param aCandidates [out] the candidate access key list.
+   *                          the first item is most preferred.
+   */
+  void GetAccessKeyCandidates(nsTArray<uint32_t>& aCandidates);
+
   static void Shutdown();
 
   /**
    * ComputeLocationFromCodeValue() returns one of .location value
    * (nsIDOMKeyEvent::DOM_KEY_LOCATION_*) which is the most preferred value
    * for the specified specified code value.
    */
   static uint32_t ComputeLocationFromCodeValue(CodeNameIndex aCodeNameIndex);
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -444,16 +444,185 @@ WidgetKeyboardEvent::ShouldCauseKeypress
     case KEY_NAME_INDEX_SymbolLock:
     case KEY_NAME_INDEX_Dead:
       return false;
     default:
       return true;
   }
 }
 
+static bool
+HasASCIIDigit(const ShortcutKeyCandidateArray& aCandidates)
+{
+  for (uint32_t i = 0; i < aCandidates.Length(); ++i) {
+    uint32_t ch = aCandidates[i].mCharCode;
+    if (ch >= '0' && ch <= '9')
+      return true;
+  }
+  return false;
+}
+
+static bool
+CharsCaseInsensitiveEqual(uint32_t aChar1, uint32_t aChar2)
+{
+  return aChar1 == aChar2 ||
+         (IS_IN_BMP(aChar1) && IS_IN_BMP(aChar2) &&
+          ToLowerCase(static_cast<char16_t>(aChar1)) ==
+            ToLowerCase(static_cast<char16_t>(aChar2)));
+}
+
+static bool
+IsCaseChangeableChar(uint32_t aChar)
+{
+  return IS_IN_BMP(aChar) &&
+         ToLowerCase(static_cast<char16_t>(aChar)) !=
+           ToUpperCase(static_cast<char16_t>(aChar));
+}
+
+void
+WidgetKeyboardEvent::GetShortcutKeyCandidates(
+                       ShortcutKeyCandidateArray& aCandidates)
+{
+  MOZ_ASSERT(aCandidates.IsEmpty(), "aCandidates must be empty");
+
+  if (mMessage != eKeyPress) {
+    return;
+  }
+
+  // ShortcutKeyCandidate::mCharCode is a candidate charCode.
+  // ShortcutKeyCandidate::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/false,
+  //   1: unshiftedCharCodes[0]/false, 2: unshiftedCharCodes[1]/false...
+  // the priority of the charCodes are (shift key is pressed):
+  //   0: charCode/false,
+  //   1: shiftedCharCodes[0]/false, 2: shiftedCharCodes[0]/true,
+  //   3: shiftedCharCodes[1]/false, 4: shiftedCharCodes[1]/true...
+  if (charCode) {
+    ShortcutKeyCandidate key(charCode, false);
+    aCandidates.AppendElement(key);
+  }
+
+  uint32_t len = alternativeCharCodes.Length();
+  if (!IsShift()) {
+    for (uint32_t i = 0; i < len; ++i) {
+      uint32_t ch = alternativeCharCodes[i].mUnshiftedCharCode;
+      if (!ch || ch == charCode) {
+        continue;
+      }
+      ShortcutKeyCandidate key(ch, false);
+      aCandidates.AppendElement(key);
+    }
+    // If unshiftedCharCodes doesn't have numeric but shiftedCharCode has it,
+    // this keyboard layout is AZERTY or similar layout, probably.
+    // In this case, Accel+[0-9] should be accessible without shift key.
+    // However, the priority should be lowest.
+    if (!HasASCIIDigit(aCandidates)) {
+      for (uint32_t i = 0; i < len; ++i) {
+        uint32_t ch = alternativeCharCodes[i].mShiftedCharCode;
+        if (ch >= '0' && ch <= '9') {
+          ShortcutKeyCandidate key(ch, false);
+          aCandidates.AppendElement(key);
+          break;
+        }
+      }
+    }
+  } else {
+    for (uint32_t i = 0; i < len; ++i) {
+      uint32_t ch = alternativeCharCodes[i].mShiftedCharCode;
+      if (!ch) {
+        continue;
+      }
+
+      if (ch != charCode) {
+        ShortcutKeyCandidate key(ch, 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.
+      uint32_t unshiftCh = alternativeCharCodes[i].mUnshiftedCharCode;
+      if (CharsCaseInsensitiveEqual(ch, unshiftCh)) {
+        continue;
+      }
+
+      // On the Hebrew keyboard layout on Windows, the unshifted char is a
+      // localized character but the shifted char is a Latin alphabet,
+      // then, we should not execute without the shift state. See bug 433192.
+      if (IsCaseChangeableChar(ch)) {
+        continue;
+      }
+
+      // Setting the alternative charCode candidates for retry without shift
+      // key state only when the shift key is pressed.
+      ShortcutKeyCandidate key(ch, true);
+      aCandidates.AppendElement(key);
+    }
+  }
+
+  // Special case for "Space" key.  With some keyboard layouts, "Space" with
+  // or without Shift key causes non-ASCII space.  For such keyboard layouts,
+  // we should guarantee that the key press works as an ASCII white space key
+  // press.
+  if (mCodeNameIndex == CODE_NAME_INDEX_Space &&
+      charCode != static_cast<uint32_t>(' ')) {
+    ShortcutKeyCandidate spaceKey(static_cast<uint32_t>(' '), false);
+    aCandidates.AppendElement(spaceKey);
+  }
+}
+
+void
+WidgetKeyboardEvent::GetAccessKeyCandidates(nsTArray<uint32_t>& aCandidates)
+{
+  MOZ_ASSERT(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 (charCode) {
+    uint32_t ch = charCode;
+    if (IS_IN_BMP(ch)) {
+      ch = ToLowerCase(static_cast<char16_t>(ch));
+    }
+    aCandidates.AppendElement(ch);
+  }
+  for (uint32_t i = 0; i < alternativeCharCodes.Length(); ++i) {
+    uint32_t ch[2] =
+      { alternativeCharCodes[i].mUnshiftedCharCode,
+        alternativeCharCodes[i].mShiftedCharCode };
+    for (uint32_t j = 0; j < 2; ++j) {
+      if (!ch[j]) {
+        continue;
+      }
+      if (IS_IN_BMP(ch[j])) {
+        ch[j] = ToLowerCase(static_cast<char16_t>(ch[j]));
+      }
+      // Don't append the charCode that was already appended.
+      if (aCandidates.IndexOf(ch[j]) == aCandidates.NoIndex) {
+        aCandidates.AppendElement(ch[j]);
+      }
+    }
+  }
+  // Special case for "Space" key.  With some keyboard layouts, "Space" with
+  // or without Shift key causes non-ASCII space.  For such keyboard layouts,
+  // we should guarantee that the key press works as an ASCII white space key
+  // press.
+  if (mCodeNameIndex == CODE_NAME_INDEX_Space &&
+      charCode != static_cast<uint32_t>(' ')) {
+    aCandidates.AppendElement(static_cast<uint32_t>(' '));
+  }
+  return;
+}
+
 /* static */ void
 WidgetKeyboardEvent::Shutdown()
 {
   delete sKeyNameIndexHashtable;
   sKeyNameIndexHashtable = nullptr;
   delete sCodeNameIndexHashtable;
   sCodeNameIndexHashtable = nullptr;
 }