Bug 1351783 part 4 - Add a KeyboardShortcut type. r=kats,masayuki draft
authorRyan Hunt <rhunt@eqrion.net>
Mon, 05 Jun 2017 18:24:35 -0500
changeset 599268 6e7da49e9459a91cd2cb43bf172ad290c2583a71
parent 599267 2db52d99287df29483ada0edc0fc739dd045d147
child 599269 0f7fa7995035961af8f52e3aa2bb7343c86ad6b9
push id65466
push userbmo:rhunt@eqrion.net
push dateThu, 22 Jun 2017 22:16:51 +0000
reviewerskats, masayuki
bugs1351783
milestone56.0a1
Bug 1351783 part 4 - Add a KeyboardShortcut type. r=kats,masayuki Keyboard scrolling works by first dispatching a key event to the focused element of the page. It is then caught by a XBL binding put on the chrome event handler of every window. The XBL binding searches through all of its handlers to find one that can handle the keyboard event. The matching binding has a command string which is dispatched to the nsGlobalWindowCommands which dispatches to PresShell which does the actual scrolling. To do this asynchronously, we need a representation of the XBL handlers that can be applied to a KeyboardInput to get a KeyboardAction. This commit adds KeyboardShortcut for this purpose. KeyboardShortcut is designed to be compatible with nsXBLPrototypeHandler and to only handle the specific cases we care about for keyboard scrolling. If a XBL handler runs javascript or does anything else we cannot handle in an OMT situation, then we create a dispatch-to-content KeyboardShortcut. MozReview-Commit-ID: 1qzywS3QHVp
dom/xbl/nsXBLEventHandler.h
dom/xbl/nsXBLPrototypeHandler.cpp
dom/xbl/nsXBLPrototypeHandler.h
dom/xbl/nsXBLWindowKeyHandler.h
gfx/layers/apz/src/Keyboard.cpp
gfx/layers/apz/src/Keyboard.h
gfx/layers/ipc/LayersMessageUtils.h
widget/TextEvents.h
--- a/dom/xbl/nsXBLEventHandler.h
+++ b/dom/xbl/nsXBLEventHandler.h
@@ -12,19 +12,17 @@
 #include "nsIDOMEventListener.h"
 #include "nsTArray.h"
 
 class nsIAtom;
 class nsIDOMKeyEvent;
 class nsXBLPrototypeHandler;
 
 namespace mozilla {
-namespace dom {
 struct IgnoreModifierState;
-} // namespace dom
 } // namespace mozilla
 
 class nsXBLEventHandler : public nsIDOMEventListener
 {
 public:
   explicit nsXBLEventHandler(nsXBLPrototypeHandler* aHandler);
 
   NS_DECL_ISUPPORTS
@@ -50,17 +48,17 @@ public:
   virtual ~nsXBLMouseEventHandler();
 
 private:
   bool EventMatched(nsIDOMEvent* aEvent) override;
 };
 
 class nsXBLKeyEventHandler : public nsIDOMEventListener
 {
-  typedef mozilla::dom::IgnoreModifierState IgnoreModifierState;
+  typedef mozilla::IgnoreModifierState IgnoreModifierState;
 
 public:
   nsXBLKeyEventHandler(nsIAtom* aEventType, uint8_t aPhase, uint8_t aType);
 
   NS_DECL_ISUPPORTS
 
   NS_DECL_NSIDOMEVENTLISTENER
 
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/ArrayUtils.h"
 
 #include "nsCOMPtr.h"
 #include "nsQueryObject.h"
 #include "nsXBLPrototypeHandler.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
+#include "nsGlobalWindowCommands.h"
 #include "nsIContent.h"
 #include "nsIAtom.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsNameSpaceManager.h"
 #include "nsIDocument.h"
 #include "nsIController.h"
 #include "nsIControllers.h"
@@ -41,23 +42,26 @@
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "nsXBLEventHandler.h"
 #include "nsXBLSerialize.h"
 #include "nsJSUtils.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/JSEventHandler.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/TextEvents.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/EventHandlerBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/layers/Keyboard.h"
 #include "xpcpublic.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::layers;
 
 uint32_t nsXBLPrototypeHandler::gRefCnt = 0;
 
 int32_t nsXBLPrototypeHandler::kMenuAccessKey = -1;
 
 const int32_t nsXBLPrototypeHandler::cShift = (1<<0);
 const int32_t nsXBLPrototypeHandler::cAlt = (1<<1);
 const int32_t nsXBLPrototypeHandler::cControl = (1<<2);
@@ -131,16 +135,76 @@ nsXBLPrototypeHandler::~nsXBLPrototypeHa
   } else if (mHandlerText) {
     free(mHandlerText);
   }
 
   // We own the next handler in the chain, so delete it now.
   NS_CONTENT_DELETE_LIST_MEMBER(nsXBLPrototypeHandler, this, mNextHandler);
 }
 
+bool
+nsXBLPrototypeHandler::TryConvertToKeyboardShortcut(
+                          KeyboardShortcut* aOut) const
+{
+  // Convert the event type
+  KeyboardInput::KeyboardEventType eventType;
+
+  if (mEventName == nsGkAtoms::keydown) {
+    eventType = KeyboardInput::KEY_DOWN;
+  } else if (mEventName == nsGkAtoms::keypress) {
+    eventType = KeyboardInput::KEY_PRESS;
+  } else if (mEventName == nsGkAtoms::keyup) {
+    eventType = KeyboardInput::KEY_UP;
+  } else {
+    return false;
+  }
+
+  // Convert the modifiers
+  Modifiers modifiersMask = GetModifiers();
+  Modifiers modifiers = GetModifiersMask();
+
+  // Mask away any bits that won't be compared
+  modifiers &= modifiersMask;
+
+  // Convert the keyCode or charCode
+  uint32_t keyCode;
+  uint32_t charCode;
+
+  if (mMisc) {
+    keyCode = 0;
+    charCode = static_cast<uint32_t>(mDetail);
+  } else {
+    keyCode = static_cast<uint32_t>(mDetail);
+    charCode = 0;
+  }
+
+  NS_LossyConvertUTF16toASCII commandText(mHandlerText);
+  KeyboardScrollAction action;
+  if (!nsGlobalWindowCommands::FindScrollCommand(commandText.get(), &action)) {
+    // This action doesn't represent a scroll so we need to create a dispatch
+    // to content keyboard shortcut so APZ handles this command correctly
+    *aOut = KeyboardShortcut(eventType,
+                             keyCode,
+                             charCode,
+                             modifiers,
+                             modifiersMask);
+    return true;
+  }
+
+  // This prototype is a command which represents a scroll action, so create
+  // a keyboard shortcut to handle it
+  *aOut = KeyboardShortcut(eventType,
+                           keyCode,
+                           charCode,
+                           modifiers,
+                           modifiersMask,
+                           action);
+  return true;
+}
+
 already_AddRefed<nsIContent>
 nsXBLPrototypeHandler::GetHandlerElement()
 {
   if (mType & NS_HANDLER_TYPE_XUL) {
     nsCOMPtr<nsIContent> element = do_QueryReferent(mHandlerElement);
     return element.forget();
   }
 
@@ -534,16 +598,64 @@ nsXBLPrototypeHandler::DispatchXULKeyCom
   keyEvent->GetMetaKey(&isMeta);
 
   nsContentUtils::DispatchXULCommand(handlerElement, true,
                                      nullptr, nullptr,
                                      isControl, isAlt, isShift, isMeta);
   return NS_OK;
 }
 
+Modifiers
+nsXBLPrototypeHandler::GetModifiers() const
+{
+  Modifiers modifiers = 0;
+
+  if (mKeyMask & cMeta) {
+    modifiers |= MODIFIER_META;
+  }
+  if (mKeyMask & cOS) {
+    modifiers |= MODIFIER_OS;
+  }
+  if (mKeyMask & cShift) {
+    modifiers |= MODIFIER_SHIFT;
+  }
+  if (mKeyMask & cAlt) {
+    modifiers |= MODIFIER_ALT;
+  }
+  if (mKeyMask & cControl) {
+    modifiers |= MODIFIER_CONTROL;
+  }
+
+  return modifiers;
+}
+
+Modifiers
+nsXBLPrototypeHandler::GetModifiersMask() const
+{
+  Modifiers modifiersMask = 0;
+
+  if (mKeyMask & cMetaMask) {
+    modifiersMask |= MODIFIER_META;
+  }
+  if (mKeyMask & cOSMask) {
+    modifiersMask |= MODIFIER_OS;
+  }
+  if (mKeyMask & cShiftMask) {
+    modifiersMask |= MODIFIER_SHIFT;
+  }
+  if (mKeyMask & cAltMask) {
+    modifiersMask |= MODIFIER_ALT;
+  }
+  if (mKeyMask & cControlMask) {
+    modifiersMask |= MODIFIER_CONTROL;
+  }
+
+  return modifiersMask;
+}
+
 already_AddRefed<nsIAtom>
 nsXBLPrototypeHandler::GetEventName()
 {
   nsCOMPtr<nsIAtom> eventName = mEventName;
   return eventName.forget();
 }
 
 already_AddRefed<nsIController>
--- a/dom/xbl/nsXBLPrototypeHandler.h
+++ b/dom/xbl/nsXBLPrototypeHandler.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 nsXBLPrototypeHandler_h__
 #define nsXBLPrototypeHandler_h__
 
+#include "mozilla/EventForwards.h"
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIController.h"
 #include "nsAutoPtr.h"
 #include "nsXBLEventHandler.h"
 #include "nsIWeakReference.h"
 #include "nsCycleCollectionParticipant.h"
@@ -22,58 +23,48 @@ class nsIContent;
 class nsIDOMUIEvent;
 class nsIDOMKeyEvent;
 class nsIDOMMouseEvent;
 class nsIObjectInputStream;
 class nsIObjectOutputStream;
 class nsXBLPrototypeBinding;
 
 namespace mozilla {
+
+struct IgnoreModifierState;
+
 namespace dom {
 class AutoJSAPI;
 class EventTarget;
 } // namespace dom
+
+namespace layers {
+class KeyboardShortcut;
+} // namespace layers
+
 } // namespace mozilla
 
 #define NS_HANDLER_TYPE_XBL_JS              (1 << 0)
 #define NS_HANDLER_TYPE_XBL_COMMAND         (1 << 1)
 #define NS_HANDLER_TYPE_XUL                 (1 << 2)
 #define NS_HANDLER_HAS_ALLOW_UNTRUSTED_ATTR (1 << 4)
 #define NS_HANDLER_ALLOW_UNTRUSTED          (1 << 5)
 #define NS_HANDLER_TYPE_SYSTEM              (1 << 6)
 #define NS_HANDLER_TYPE_PREVENTDEFAULT      (1 << 7)
 
 // XXX Use nsIDOMEvent:: codes?
 #define NS_PHASE_CAPTURING          1
 #define NS_PHASE_TARGET             2
 #define NS_PHASE_BUBBLING           3
 
-namespace mozilla {
-namespace dom {
-
-struct IgnoreModifierState
-{
-  // When mShift is true, Shift key state will be ignored.
-  bool mShift;
-  // When mOS is true, OS key state will be ignored.
-  bool mOS;
-
-  IgnoreModifierState()
-    : mShift(false)
-    , mOS(false)
-  {
-  }
-};
-
-} // namespace dom
-} // namespace mozilla
-
 class nsXBLPrototypeHandler
 {
-  typedef mozilla::dom::IgnoreModifierState IgnoreModifierState;
+  typedef mozilla::IgnoreModifierState IgnoreModifierState;
+  typedef mozilla::layers::KeyboardShortcut KeyboardShortcut;
+  typedef mozilla::Modifiers Modifiers;
 
 public:
   // This constructor is used by XBL handlers (both the JS and command shorthand variety)
   nsXBLPrototypeHandler(const char16_t* aEvent, const char16_t* aPhase,
                         const char16_t* aAction, const char16_t* aCommand,
                         const char16_t* aKeyCode, const char16_t* aCharCode,
                         const char16_t* aModifiers, const char16_t* aButton,
                         const char16_t* aClickCount, const char16_t* aGroup,
@@ -85,16 +76,27 @@ public:
   // This constructor is used only by XUL key handlers (e.g., <key>)
   explicit nsXBLPrototypeHandler(nsIContent* aKeyElement, bool aReserved);
 
   // This constructor is used for handlers loaded from the cache
   explicit nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding);
 
   ~nsXBLPrototypeHandler();
 
+  /**
+   * Try and convert this XBL handler into an APZ KeyboardShortcut for handling
+   * key events on the compositor thread. This only works for XBL handlers that
+   * represent scroll commands.
+   *
+   * @param aOut the converted KeyboardShortcut, must be non null
+   * @return whether the handler was converted into a KeyboardShortcut
+   */
+  bool TryConvertToKeyboardShortcut(
+          KeyboardShortcut* aOut) const;
+
   bool EventTypeEquals(nsIAtom* aEventType) const
   {
     return mEventName == aEventType;
   }
 
   // if aCharCode is not zero, it is used instead of the charCode of aKeyEvent.
   bool KeyEventMatched(nsIDOMKeyEvent* aKeyEvent,
                        uint32_t aCharCode,
@@ -181,16 +183,20 @@ protected:
   void ReportKeyConflict(const char16_t* aKey, const char16_t* aModifiers, nsIContent* aElement, const char *aMessageName);
   void GetEventType(nsAString& type);
   bool ModifiersMatchMask(nsIDOMUIEvent* aEvent,
                           const IgnoreModifierState& aIgnoreModifierState);
   nsresult DispatchXBLCommand(mozilla::dom::EventTarget* aTarget, nsIDOMEvent* aEvent);
   nsresult DispatchXULKeyCommand(nsIDOMEvent* aEvent);
   nsresult EnsureEventHandler(mozilla::dom::AutoJSAPI& jsapi, nsIAtom* aName,
                               JS::MutableHandle<JSObject*> aHandler);
+
+  Modifiers GetModifiers() const;
+  Modifiers GetModifiersMask() const;
+
   static int32_t KeyToMask(int32_t key);
   static int32_t AccelKeyMask();
 
   static int32_t kMenuAccessKey;
   static void InitAccessKeys();
 
   static const int32_t cShift;
   static const int32_t cAlt;
--- a/dom/xbl/nsXBLWindowKeyHandler.h
+++ b/dom/xbl/nsXBLWindowKeyHandler.h
@@ -14,27 +14,27 @@
 class nsIAtom;
 class nsIDOMElement;
 class nsIDOMKeyEvent;
 class nsXBLSpecialDocInfo;
 class nsXBLPrototypeHandler;
 
 namespace mozilla {
 class EventListenerManager;
+struct IgnoreModifierState;
 namespace dom {
 class Element;
 class EventTarget;
-struct IgnoreModifierState;
 } // namespace dom
 } // namespace mozilla
 
 class nsXBLWindowKeyHandler : public nsIDOMEventListener
 {
-  typedef mozilla::dom::IgnoreModifierState IgnoreModifierState;
   typedef mozilla::EventListenerManager EventListenerManager;
+  typedef mozilla::IgnoreModifierState IgnoreModifierState;
 
 public:
   nsXBLWindowKeyHandler(nsIDOMElement* aElement, mozilla::dom::EventTarget* aTarget);
 
   void InstallKeyboardEventListenersTo(
          EventListenerManager* aEventListenerManager);
   void RemoveKeyboardEventListenersFrom(
          EventListenerManager* aEventListenerManager);
--- a/gfx/layers/apz/src/Keyboard.cpp
+++ b/gfx/layers/apz/src/Keyboard.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 #include "mozilla/layers/Keyboard.h"
 
+#include "mozilla/TextEvents.h" // for IgnoreModifierState
+
 namespace mozilla {
 namespace layers {
 
 /* static */ nsIScrollableFrame::ScrollUnit
 KeyboardScrollAction::GetScrollUnit(KeyboardScrollAction::KeyboardScrollActionType aDeltaType)
 {
   switch (aDeltaType) {
     case KeyboardScrollAction::eScrollCharacter:
@@ -33,10 +35,100 @@ KeyboardScrollAction::KeyboardScrollActi
 }
 
 KeyboardScrollAction::KeyboardScrollAction(KeyboardScrollActionType aType, bool aForward)
   : mType(aType)
   , mForward(aForward)
 {
 }
 
+KeyboardShortcut::KeyboardShortcut()
+{
+}
+
+KeyboardShortcut::KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType,
+                                   uint32_t aKeyCode,
+                                   uint32_t aCharCode,
+                                   Modifiers aModifiers,
+                                   Modifiers aModifiersMask,
+                                   const KeyboardScrollAction& aAction)
+  : mAction(aAction)
+  , mKeyCode(aKeyCode)
+  , mCharCode(aCharCode)
+  , mModifiers(aModifiers)
+  , mModifiersMask(aModifiersMask)
+  , mEventType(aEventType)
+  , mDispatchToContent(false)
+{
+}
+
+KeyboardShortcut::KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType,
+                                   uint32_t aKeyCode,
+                                   uint32_t aCharCode,
+                                   Modifiers aModifiers,
+                                   Modifiers aModifiersMask)
+  : mKeyCode(aKeyCode)
+  , mCharCode(aCharCode)
+  , mModifiers(aModifiers)
+  , mModifiersMask(aModifiersMask)
+  , mEventType(aEventType)
+  , mDispatchToContent(true)
+{
+}
+
+bool
+KeyboardShortcut::Matches(const KeyboardInput& aInput,
+                          const IgnoreModifierState& aIgnore,
+                          uint32_t aOverrideCharCode) const
+{
+  return mEventType == aInput.mType &&
+         MatchesKey(aInput, aOverrideCharCode) &&
+         MatchesModifiers(aInput, aIgnore);
+}
+
+bool
+KeyboardShortcut::MatchesKey(const KeyboardInput& aInput,
+                             uint32_t aOverrideCharCode) const
+{
+  // Compare by the key code if we have one
+  if (!mCharCode) {
+    return mKeyCode == aInput.mKeyCode;
+  }
+
+  // We are comparing by char code
+  uint32_t charCode;
+
+  // If we are comparing against a shortcut candidate then we might
+  // have an override char code
+  if (aOverrideCharCode) {
+    charCode = aOverrideCharCode;
+  } else {
+    charCode = aInput.mCharCode;
+  }
+
+  // Both char codes must be in lowercase to compare correctly
+  if (IS_IN_BMP(charCode)) {
+    charCode = ToLowerCase(static_cast<char16_t>(charCode));
+  }
+
+  return mCharCode == charCode;
+}
+
+bool
+KeyboardShortcut::MatchesModifiers(const KeyboardInput& aInput,
+                                   const IgnoreModifierState& aIgnore) const
+{
+  Modifiers modifiersMask = mModifiersMask;
+
+  // If we are ignoring Shift or OS, then unset that part of the mask
+  if (aIgnore.mOS) {
+    modifiersMask &= ~MODIFIER_OS;
+  }
+  if (aIgnore.mShift) {
+    modifiersMask &= ~MODIFIER_SHIFT;
+  }
+
+  // Mask off the modifiers we are ignoring from the keyboard input
+  return (aInput.modifiers & modifiersMask) == mModifiers;
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/Keyboard.h
+++ b/gfx/layers/apz/src/Keyboard.h
@@ -1,19 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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_layers_Keyboard_h
 #define mozilla_layers_Keyboard_h
 
+#include <stdint.h> // for uint32_t
+
+#include "InputData.h"          // for KeyboardInput
 #include "nsIScrollableFrame.h" // for nsIScrollableFrame::ScrollUnit
 
 namespace mozilla {
+
+struct IgnoreModifierState;
+
 namespace layers {
 
 /**
  * This class represents a scrolling action to be performed on a scrollable layer.
  */
 struct KeyboardScrollAction final
 {
 public:
@@ -35,12 +41,73 @@ public:
   KeyboardScrollAction(KeyboardScrollActionType aType, bool aForward);
 
   // The type of scroll to perform for this action
   KeyboardScrollActionType mType;
   // Whether to scroll forward or backward along the axis of this action type
   bool mForward;
 };
 
+/**
+ * This class is an off main-thread <xul:handler> for scrolling commands.
+ */
+class KeyboardShortcut final
+{
+public:
+  KeyboardShortcut();
+
+  /**
+   * Create a keyboard shortcut that when matched can be handled by executing
+   * the specified keyboard action.
+   */
+  KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType,
+                   uint32_t aKeyCode,
+                   uint32_t aCharCode,
+                   Modifiers aModifiers,
+                   Modifiers aModifiersMask,
+                   const KeyboardScrollAction& aAction);
+
+  /**
+   * Create a keyboard shortcut that when matched should be handled by ignoring
+   * the keyboard event and dispatching it to content.
+   */
+  KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType,
+                   uint32_t aKeyCode,
+                   uint32_t aCharCode,
+                   Modifiers aModifiers,
+                   Modifiers aModifiersMask);
+
+  bool Matches(const KeyboardInput& aInput,
+               const IgnoreModifierState& aIgnore,
+               uint32_t aOverrideCharCode = 0) const;
+
+private:
+  bool MatchesKey(const KeyboardInput& aInput,
+                  uint32_t aOverrideCharCode) const;
+  bool MatchesModifiers(const KeyboardInput& aInput,
+                        const IgnoreModifierState& aIgnore) const;
+
+public:
+  // The action to perform when this shortcut is matched,
+  // and not flagged to be dispatched to content
+  KeyboardScrollAction mAction;
+
+  // Only one of mKeyCode or mCharCode may be non-zero
+  // whichever one is non-zero is the one to compare when matching
+  uint32_t mKeyCode;
+  uint32_t mCharCode;
+
+  // The modifiers that must be active for this shortcut
+  Modifiers mModifiers;
+  // The modifiers to compare when matching this shortcut
+  Modifiers mModifiersMask;
+
+  // The type of keyboard event to match against
+  KeyboardInput::KeyboardEventType mEventType;
+
+  // Whether events matched by this must be dispatched to content
+  bool mDispatchToContent;
+};
+
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_Keyboard_h
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -438,16 +438,44 @@ struct ParamTraits<mozilla::layers::Keyb
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->mType) &&
            ReadParam(aMsg, aIter, &aResult->mForward);
   }
 };
 
+template <>
+struct ParamTraits<mozilla::layers::KeyboardShortcut>
+{
+  typedef mozilla::layers::KeyboardShortcut paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mAction);
+    WriteParam(aMsg, aParam.mKeyCode);
+    WriteParam(aMsg, aParam.mCharCode);
+    WriteParam(aMsg, aParam.mModifiers);
+    WriteParam(aMsg, aParam.mModifiersMask);
+    WriteParam(aMsg, aParam.mEventType);
+    WriteParam(aMsg, aParam.mDispatchToContent);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->mAction) &&
+           ReadParam(aMsg, aIter, &aResult->mKeyCode) &&
+           ReadParam(aMsg, aIter, &aResult->mCharCode) &&
+           ReadParam(aMsg, aIter, &aResult->mModifiers) &&
+           ReadParam(aMsg, aIter, &aResult->mModifiersMask) &&
+           ReadParam(aMsg, aIter, &aResult->mEventType) &&
+           ReadParam(aMsg, aIter, &aResult->mDispatchToContent);
+  }
+};
+
 typedef mozilla::layers::GeckoContentController::TapType TapType;
 
 template <>
 struct ParamTraits<TapType>
   : public ContiguousEnumSerializer<
              TapType,
              TapType::eSingleTap,
              TapType::eSentinel>
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -101,16 +101,37 @@ struct ShortcutKeyCandidate
   // The mCharCode 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::IgnoreModifierState
+ *
+ * This stores flags for modifiers that should be ignored when matching
+ * XBL handlers.
+ ******************************************************************************/
+
+struct IgnoreModifierState
+{
+  // When mShift is true, Shift key state will be ignored.
+  bool mShift;
+  // When mOS is true, OS key state will be ignored.
+  bool mOS;
+
+  IgnoreModifierState()
+    : mShift(false)
+    , mOS(false)
+  {
+  }
+};
+
+/******************************************************************************
  * mozilla::WidgetKeyboardEvent
  ******************************************************************************/
 
 class WidgetKeyboardEvent : public WidgetInputEvent
 {
 private:
   friend class dom::PBrowserParent;
   friend class dom::PBrowserChild;