Bug 1351783 part 16 - Perform async scrolling for keyboard events when possible. r?kats,botond draft
authorRyan Hunt <rhunt@eqrion.net>
Mon, 05 Jun 2017 19:46:06 -0500
changeset 599280 426e9a2b3d5eb9143f74e3881cc1a182ded5c1a6
parent 599279 b02717218b46daf255d35e04091c7982165040ed
child 599281 c31bc0f5c265b67271b14386f55151be3aa5e623
push id65466
push userbmo:rhunt@eqrion.net
push dateThu, 22 Jun 2017 22:16:51 +0000
reviewerskats, botond
bugs1351783
milestone56.0a1
Bug 1351783 part 16 - Perform async scrolling for keyboard events when possible. r?kats,botond This commit ties it all together by dispatching keyboard actions to scroll targets in response to keyboard inputs when we have current and valid focus state. MozReview-Commit-ID: G7rZiS3FH5e
gfx/layers/apz/public/IAPZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/apz/src/FocusState.cpp
gfx/layers/apz/src/FocusState.h
gfx/layers/apz/src/InputBlockState.cpp
gfx/layers/apz/src/InputBlockState.h
gfx/layers/apz/src/InputQueue.cpp
gfx/layers/apz/src/InputQueue.h
gfx/layers/apz/src/QueuedInput.cpp
gfx/layers/apz/src/QueuedInput.h
gfx/layers/ipc/APZCTreeManagerChild.cpp
gfx/layers/ipc/APZCTreeManagerParent.cpp
gfx/layers/ipc/APZCTreeManagerParent.h
gfx/layers/ipc/PAPZCTreeManager.ipdl
gfx/thebes/gfxPrefs.h
ipc/ipdl/sync-messages.ini
modules/libpref/init/all.js
--- a/gfx/layers/apz/public/IAPZCTreeManager.cpp
+++ b/gfx/layers/apz/public/IAPZCTreeManager.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/layers/IAPZCTreeManager.h"
 
 #include "gfxPrefs.h"                       // for gfxPrefs
 #include "InputData.h"                      // for InputData, etc
 #include "mozilla/EventStateManager.h"      // for WheelPrefs
 #include "mozilla/layers/APZThreadUtils.h"  // for AssertOnCompositorThread, etc
 #include "mozilla/MouseEvents.h"            // for WidgetMouseEvent
+#include "mozilla/TextEvents.h"             // for WidgetKeyboardEvent
 #include "mozilla/TouchEvents.h"            // for WidgetTouchEvent
 
 namespace mozilla {
 namespace layers {
 
 static bool
 WillHandleMouseEvent(const WidgetMouseEventBase& aEvent)
 {
@@ -139,16 +140,27 @@ IAPZCTreeManager::ReceiveInputEvent(
         return status;
       }
 
       UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
       ProcessUnhandledEvent(&aEvent.mRefPoint, aOutTargetGuid, &aEvent.mFocusSequenceNumber);
       return nsEventStatus_eIgnore;
 
     }
+    case eKeyboardEventClass: {
+      WidgetKeyboardEvent& keyboardEvent = *aEvent.AsKeyboardEvent();
+
+      KeyboardInput input(keyboardEvent);
+
+      nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
+
+      keyboardEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
+      keyboardEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
+      return status;
+    }
     default: {
 
       UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
       ProcessUnhandledEvent(&aEvent.mRefPoint, aOutTargetGuid, &aEvent.mFocusSequenceNumber);
       return nsEventStatus_eIgnore;
 
     }
   }
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1233,16 +1233,101 @@ APZCTreeManager::ReceiveInputEvent(Input
             /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
             tapInput, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
         tapInput.mPoint = *untransformedPoint;
       }
       break;
+    } case KEYBOARD_INPUT: {
+      // Disable async keyboard scrolling when accessibility.browsewithcaret is enabled
+      if (!gfxPrefs::APZKeyboardEnabled() ||
+          gfxPrefs::AccessibilityBrowseWithCaret()) {
+        return result;
+      }
+
+      KeyboardInput& keyInput = aEvent.AsKeyboardInput();
+
+      // Try and find a matching shortcut for this keyboard input
+      Maybe<KeyboardShortcut> shortcut = mKeyboardMap.FindMatch(keyInput);
+
+      if (!shortcut) {
+        // If we don't have a shortcut for this key event, then we can keep our focus
+        // only if we know there are no key event listeners for this target
+        if (mFocusState.CanIgnoreKeyboardShortcutMisses()) {
+          focusSetter.MarkAsNonFocusChanging();
+        }
+        return result;
+      }
+
+      // Check if this shortcut needs to be dispatched to content. Anything matching
+      // this is assumed to be able to change focus.
+      if (shortcut->mDispatchToContent) {
+        return result;
+      }
+
+      // We know we have an action to execute on whatever is the current focus target
+      const KeyboardScrollAction& action = shortcut->mAction;
+
+      // The current focus target depends on which direction the scroll is to happen
+      Maybe<ScrollableLayerGuid> targetGuid;
+      switch (action.mType)
+      {
+        case KeyboardScrollAction::eScrollCharacter: {
+          targetGuid = mFocusState.GetHorizontalTarget();
+          break;
+        }
+        case KeyboardScrollAction::eScrollLine:
+        case KeyboardScrollAction::eScrollPage:
+        case KeyboardScrollAction::eScrollComplete: {
+          targetGuid = mFocusState.GetVerticalTarget();
+          break;
+        }
+
+        case KeyboardScrollAction::eSentinel: {
+          MOZ_ASSERT_UNREACHABLE("Invalid KeyboardScrollActionType");
+        }
+      }
+
+      // If we don't have a scroll target then either we have a stale focus target,
+      // the focused element has event listeners, or the focused element doesn't have a
+      // layerized scroll frame. In any case we need to dispatch to content.
+      if (!targetGuid) {
+        return result;
+      }
+
+      RefPtr<AsyncPanZoomController> targetApzc = GetTargetAPZC(targetGuid->mLayersId,
+                                                                targetGuid->mScrollId);
+
+      // Scroll snapping behavior with keyboard input is more complicated, so
+      // ignore any input events that are targeted at an Apzc with scroll snap
+      // points.
+      if (!targetApzc || targetApzc->HasScrollSnapping()) {
+        return result;
+      }
+
+      // Attach the keyboard scroll action to the input event for processing
+      // by the input queue.
+      keyInput.mAction = action;
+
+      // Dispatch the event to the input queue.
+      result = mInputQueue->ReceiveInputEvent(
+          targetApzc,
+          /* aTargetConfirmed = */ true,
+          keyInput, aOutInputBlockId);
+
+      // Any keyboard event that is dispatched to the input queue at this point
+      // should have been consumed
+      MOZ_ASSERT(result == nsEventStatus_eConsumeNoDefault);
+
+      keyInput.mHandledByAPZ = true;
+      focusSetter.MarkAsNonFocusChanging();
+
+      break;
     } case SENTINEL_INPUT: {
       MOZ_ASSERT_UNREACHABLE("Invalid InputType.");
       break;
     }
   }
   return result;
 }
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -336,16 +336,23 @@ public:
 
   /**
    * Returns whether this APZC is for an element marked with the 'scrollgrab'
    * attribute.
    */
   bool HasScrollgrab() const { return mScrollMetadata.GetHasScrollgrab(); }
 
   /**
+   * Returns whether this APZC has scroll snapping points.
+   */
+  bool HasScrollSnapping() const {
+    return mScrollMetadata.GetSnapInfo().HasScrollSnapping();
+  }
+
+  /**
    * Returns whether this APZC has room to be panned (in any direction).
    */
   bool IsPannable() const;
 
   /**
    * Returns whether this APZC represents a scroll info layer.
    */
   bool IsScrollInfoLayer() const;
--- a/gfx/layers/apz/src/FocusState.cpp
+++ b/gfx/layers/apz/src/FocusState.cpp
@@ -108,10 +108,40 @@ FocusState::GetFocusTargetLayerIds() con
 }
 
 void
 FocusState::RemoveFocusTarget(uint64_t aLayersId)
 {
   mFocusTree.erase(aLayersId);
 }
 
+Maybe<ScrollableLayerGuid>
+FocusState::GetHorizontalTarget() const
+{
+  // There is not a scrollable layer to async scroll if
+  //   1. We aren't current
+  //   2. There are event listeners that could change the focus
+  //   3. The target has not been layerized
+  if (!IsCurrent() ||
+      mFocusHasKeyEventListeners ||
+      mFocusHorizontalTarget == FrameMetrics::NULL_SCROLL_ID) {
+    return Nothing();
+  }
+  return Some(ScrollableLayerGuid(mFocusLayersId, 0, mFocusHorizontalTarget));
+}
+
+Maybe<ScrollableLayerGuid>
+FocusState::GetVerticalTarget() const
+{
+  // There is not a scrollable layer to async scroll if:
+  //   1. We aren't current
+  //   2. There are event listeners that could change the focus
+  //   3. The target has not been layerized
+  if (!IsCurrent() ||
+      mFocusHasKeyEventListeners ||
+      mFocusVerticalTarget == FrameMetrics::NULL_SCROLL_ID) {
+    return Nothing();
+  }
+  return Some(ScrollableLayerGuid(mFocusLayersId, 0, mFocusVerticalTarget));
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/FocusState.h
+++ b/gfx/layers/apz/src/FocusState.h
@@ -110,16 +110,41 @@ public:
    */
   std::unordered_set<uint64_t> GetFocusTargetLayerIds() const;
 
   /**
    * Removes a focus target by its layer tree ID.
    */
   void RemoveFocusTarget(uint64_t aLayersId);
 
+  /**
+   * Gets the scrollable layer that should be horizontally scrolled for a key
+   * event, if any. The returned ScrollableLayerGuid doesn't contain a presShellId,
+   * and so it should not be used in comparisons.
+   *
+   * No scrollable layer is returned if any of the following are true:
+   *   1. We don't have a current focus target
+   *   2. There are event listeners that could change the focus
+   *   3. The target has not been layerized
+   */
+  Maybe<ScrollableLayerGuid> GetHorizontalTarget() const;
+  /**
+   * The same as GetHorizontalTarget() but for vertical scrolling.
+   */
+  Maybe<ScrollableLayerGuid> GetVerticalTarget() const;
+
+  /**
+   * Gets whether it is safe to not increment the focus sequence number for an
+   * unmatched keyboard event.
+   */
+  bool CanIgnoreKeyboardShortcutMisses() const
+  {
+    return IsCurrent() && !mFocusHasKeyEventListeners;
+  }
+
 private:
   // The set of focus targets received indexed by their layer tree ID
   std::unordered_map<uint64_t, FocusTarget> mFocusTree;
 
   // The focus sequence number of the last potentially focus changing event
   // processed by APZ. This number starts at one and increases monotonically.
   // We don't worry about wrap around here because at a pace of 100 increments/sec,
   // it would take 5.85*10^9 years before we would wrap around. This number will
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -865,10 +865,15 @@ TouchBlockState::UpdateSlopState(const M
 }
 
 uint32_t
 TouchBlockState::GetActiveTouchCount() const
 {
   return mTouchCounter.GetActiveTouchCount();
 }
 
+KeyboardBlockState::KeyboardBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc)
+  : InputBlockState(aTargetApzc, true)
+{
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -22,16 +22,17 @@ namespace layers {
 
 class AsyncPanZoomController;
 class OverscrollHandoffChain;
 class CancelableBlockState;
 class TouchBlockState;
 class WheelBlockState;
 class DragBlockState;
 class PanGestureBlockState;
+class KeyboardBlockState;
 
 /**
  * A base class that stores state common to various input blocks.
  * Note that the InputBlockState constructor acquires the tree lock, so callers
  * from inside AsyncPanZoomController should ensure that the APZC lock is not
  * held.
  */
 class InputBlockState : public RefCounted<InputBlockState>
@@ -63,16 +64,19 @@ public:
     return nullptr;
   }
   virtual DragBlockState* AsDragBlock() {
     return nullptr;
   }
   virtual PanGestureBlockState* AsPanGestureBlock() {
     return nullptr;
   }
+  virtual KeyboardBlockState* AsKeyboardBlock() {
+    return nullptr;
+  }
 
   virtual bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
                                       TargetConfirmationState aState,
                                       InputData* aFirstInput);
   const RefPtr<AsyncPanZoomController>& GetTargetApzc() const;
   const RefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
   uint64_t GetBlockId() const;
 
@@ -481,12 +485,29 @@ private:
   bool mDuringFastFling;
   bool mSingleTapOccurred;
   bool mInSlop;
   ScreenIntPoint mSlopOrigin;
   // A reference to the InputQueue's touch counter
   TouchCounter& mTouchCounter;
 };
 
+/**
+ * This class represents a set of keyboard inputs targeted at the same Apzc.
+ */
+class KeyboardBlockState : public InputBlockState
+{
+public:
+  explicit KeyboardBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc);
+
+  KeyboardBlockState* AsKeyboardBlock() override {
+    return this;
+  }
+
+  bool MustStayActive() override {
+    return false;
+  }
+};
+
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_InputBlockState_h
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -51,16 +51,24 @@ InputQueue::ReceiveInputEvent(const RefP
       return ReceivePanGestureInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
     }
 
     case MOUSE_INPUT: {
       const MouseInput& event = aEvent.AsMouseInput();
       return ReceiveMouseInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
     }
 
+    case KEYBOARD_INPUT: {
+      // Every keyboard input must have a confirmed target
+      MOZ_ASSERT(aTarget && aTargetConfirmed);
+
+      const KeyboardInput& event = aEvent.AsKeyboardInput();
+      return ReceiveKeyboardInput(aTarget, event, aOutInputBlockId);
+    }
+
     default:
       // The return value for non-touch input is only used by tests, so just pass
       // through the return value for now. This can be changed later if needed.
       // TODO (bug 1098430): we will eventually need to have smarter handling for
       // non-touch events as well.
       return aTarget->HandleInputEvent(aEvent, aTarget->GetTransformToThis());
   }
 }
@@ -263,16 +271,49 @@ InputQueue::ReceiveScrollWheelInput(cons
   // |aEvent|.
   block->Update(mQueuedInputs.LastElement()->Input()->AsScrollWheelInput());
 
   ProcessQueue();
 
   return nsEventStatus_eConsumeDoDefault;
 }
 
+nsEventStatus
+InputQueue::ReceiveKeyboardInput(const RefPtr<AsyncPanZoomController>& aTarget,
+                                 const KeyboardInput& aEvent,
+                                 uint64_t* aOutInputBlockId) {
+  KeyboardBlockState* block = mActiveKeyboardBlock.get();
+
+  // If the block is targeting a different Apzc than this keyboard event then
+  // we'll create a new input block
+  if (block && block->GetTargetApzc() != aTarget) {
+    block = nullptr;
+  }
+
+  if (!block) {
+    block = new KeyboardBlockState(aTarget);
+    INPQ_LOG("started new keyboard block %p id %" PRIu64 " for target %p\n",
+        block, block->GetBlockId(), aTarget.get());
+
+    mActiveKeyboardBlock = block;
+  } else {
+    INPQ_LOG("received new event in block %p\n", block);
+  }
+
+  if (aOutInputBlockId) {
+    *aOutInputBlockId = block->GetBlockId();
+  }
+
+  mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
+
+  ProcessQueue();
+
+  return nsEventStatus_eConsumeNoDefault;
+}
+
 static bool
 CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent,
                             PanGestureBlockState* aBlock)
 {
   PanGestureInput horizontalComponent = aInitialEvent;
   horizontalComponent.mPanDisplacement.y = 0;
   RefPtr<AsyncPanZoomController> horizontallyScrollableAPZC =
     aBlock->GetOverscrollHandoffChain()->FindFirstScrollable(horizontalComponent);
@@ -456,16 +497,23 @@ InputQueue::GetCurrentDragBlock() const
 
 PanGestureBlockState*
 InputQueue::GetCurrentPanGestureBlock() const
 {
   InputBlockState* block = GetCurrentBlock();
   return block ? block->AsPanGestureBlock() : mActivePanGestureBlock.get();
 }
 
+KeyboardBlockState*
+InputQueue::GetCurrentKeyboardBlock() const
+{
+  InputBlockState* block = GetCurrentBlock();
+  return block ? block->AsKeyboardBlock() : mActiveKeyboardBlock.get();
+}
+
 WheelBlockState*
 InputQueue::GetActiveWheelTransaction() const
 {
   WheelBlockState* block = mActiveWheelBlock.get();
   if (!block || !block->InTransaction()) {
     return nullptr;
   }
   return block;
--- a/gfx/layers/apz/src/InputQueue.h
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -25,16 +25,17 @@ namespace layers {
 
 class AsyncPanZoomController;
 class InputBlockState;
 class CancelableBlockState;
 class TouchBlockState;
 class WheelBlockState;
 class DragBlockState;
 class PanGestureBlockState;
+class KeyboardBlockState;
 class AsyncDragMetrics;
 class QueuedInput;
 
 /**
  * This class stores incoming input events, associated with "input blocks", until
  * they are ready for handling.
  */
 class InputQueue {
@@ -101,16 +102,17 @@ public:
    * mActiveXXXBlock field of the corresponding input type to see if there is
    * a depleted but still active input block, and returns that if found. These
    * functions may return null if no block is found.
    */
   TouchBlockState* GetCurrentTouchBlock() const;
   WheelBlockState* GetCurrentWheelBlock() const;
   DragBlockState* GetCurrentDragBlock() const;
   PanGestureBlockState* GetCurrentPanGestureBlock() const;
+  KeyboardBlockState* GetCurrentKeyboardBlock() const;
   /**
    * Returns true iff the pending block at the head of the queue is a touch
    * block and is ready for handling.
    */
   bool HasReadyTouchBlock() const;
   /**
    * If there is an active wheel transaction, returns the WheelBlockState
    * representing the transaction. Otherwise, returns null. "Active" in this
@@ -164,16 +166,19 @@ private:
   nsEventStatus ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarget,
                                         bool aTargetConfirmed,
                                         const ScrollWheelInput& aEvent,
                                         uint64_t* aOutInputBlockId);
   nsEventStatus ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget,
                                         bool aTargetConfirmed,
                                         const PanGestureInput& aEvent,
                                         uint64_t* aOutInputBlockId);
+  nsEventStatus ReceiveKeyboardInput(const RefPtr<AsyncPanZoomController>& aTarget,
+                                     const KeyboardInput& aEvent,
+                                     uint64_t* aOutInputBlockId);
 
   /**
    * Helper function that searches mQueuedInputs for the first block matching
    * the given id, and returns it. If |aOutFirstInput| is non-null, it is
    * populated with a pointer to the first input in mQueuedInputs that
    * corresponds to the block, or null if no such input was found. Note that
    * even if there are no inputs in mQueuedInputs, this function can return
    * non-null if the block id provided matches one of the depleted-but-still-
@@ -197,16 +202,17 @@ private:
   // "active" in the sense that new inputs of that type are associated with
   // them. Note that these pointers may be null if no inputs of the type have
   // arrived, or if the inputs for the type formed a complete block that was
   // then discarded.
   RefPtr<TouchBlockState> mActiveTouchBlock;
   RefPtr<WheelBlockState> mActiveWheelBlock;
   RefPtr<DragBlockState> mActiveDragBlock;
   RefPtr<PanGestureBlockState> mActivePanGestureBlock;
+  RefPtr<KeyboardBlockState> mActiveKeyboardBlock;
 
   // The APZC to which the last event was delivered
   RefPtr<AsyncPanZoomController> mLastActiveApzc;
 
   // Track touches so we know when to clear mLastActiveApzc
   TouchCounter mTouchCounter;
 
   // Track mouse inputs so we know if we're in a drag or not
--- a/gfx/layers/apz/src/QueuedInput.cpp
+++ b/gfx/layers/apz/src/QueuedInput.cpp
@@ -33,16 +33,22 @@ QueuedInput::QueuedInput(const MouseInpu
 }
 
 QueuedInput::QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aBlock)
   : mInput(MakeUnique<PanGestureInput>(aInput))
   , mBlock(&aBlock)
 {
 }
 
+QueuedInput::QueuedInput(const KeyboardInput& aInput, KeyboardBlockState& aBlock)
+  : mInput(MakeUnique<KeyboardInput>(aInput))
+  , mBlock(&aBlock)
+{
+}
+
 InputData*
 QueuedInput::Input()
 {
   return mInput.get();
 }
 
 InputBlockState*
 QueuedInput::Block()
--- a/gfx/layers/apz/src/QueuedInput.h
+++ b/gfx/layers/apz/src/QueuedInput.h
@@ -20,29 +20,31 @@ class PanGestureInput;
 
 namespace layers {
 
 class InputBlockState;
 class TouchBlockState;
 class WheelBlockState;
 class DragBlockState;
 class PanGestureBlockState;
+class KeyboardBlockState;
 
 /**
  * This lightweight class holds a pointer to an input event that has not yet
  * been completely processed, along with the input block that the input event
  * is associated with.
  */
 class QueuedInput
 {
 public:
   QueuedInput(const MultiTouchInput& aInput, TouchBlockState& aBlock);
   QueuedInput(const ScrollWheelInput& aInput, WheelBlockState& aBlock);
   QueuedInput(const MouseInput& aInput, DragBlockState& aBlock);
   QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aBlock);
+  QueuedInput(const KeyboardInput& aInput, KeyboardBlockState& aBlock);
 
   InputData* Input();
   InputBlockState* Block();
 
 private:
   // A copy of the input event that is provided to the constructor. This must
   // be non-null, and is owned by this QueuedInput instance (hence the
   // UniquePtr).
--- a/gfx/layers/ipc/APZCTreeManagerChild.cpp
+++ b/gfx/layers/ipc/APZCTreeManagerChild.cpp
@@ -114,16 +114,30 @@ APZCTreeManagerChild::ReceiveInputEvent(
                                      &res,
                                      &processedEvent,
                                      aOutTargetGuid,
                                      aOutInputBlockId);
 
     event = processedEvent;
     return res;
   }
+  case KEYBOARD_INPUT: {
+    KeyboardInput& event = aEvent.AsKeyboardInput();
+    KeyboardInput processedEvent;
+
+    nsEventStatus res;
+    SendReceiveKeyboardInputEvent(event,
+                                  &res,
+                                  &processedEvent,
+                                  aOutTargetGuid,
+                                  aOutInputBlockId);
+
+    event = processedEvent;
+    return res;
+  }
   default: {
     MOZ_ASSERT_UNREACHABLE("Invalid InputData type.");
     return nsEventStatus_eConsumeNoDefault;
   }
   }
 }
 
 void
--- a/gfx/layers/ipc/APZCTreeManagerParent.cpp
+++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp
@@ -140,16 +140,35 @@ APZCTreeManagerParent::RecvReceiveScroll
     aOutTargetGuid,
     aOutInputBlockId);
   *aOutEvent = event;
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+APZCTreeManagerParent::RecvReceiveKeyboardInputEvent(
+        const KeyboardInput& aEvent,
+        nsEventStatus* aOutStatus,
+        KeyboardInput* aOutEvent,
+        ScrollableLayerGuid* aOutTargetGuid,
+        uint64_t* aOutInputBlockId)
+{
+  KeyboardInput event = aEvent;
+
+  *aOutStatus = mTreeManager->ReceiveInputEvent(
+    event,
+    aOutTargetGuid,
+    aOutInputBlockId);
+  *aOutEvent = event;
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap)
 {
   mTreeManager->SetKeyboardMap(aKeyboardMap);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
--- a/gfx/layers/ipc/APZCTreeManagerParent.h
+++ b/gfx/layers/ipc/APZCTreeManagerParent.h
@@ -74,16 +74,24 @@ public:
   RecvReceiveScrollWheelInputEvent(
           const ScrollWheelInput& aEvent,
           nsEventStatus* aOutStatus,
           ScrollWheelInput* aOutEvent,
           ScrollableLayerGuid* aOutTargetGuid,
           uint64_t* aOutInputBlockId) override;
 
   mozilla::ipc::IPCResult
+  RecvReceiveKeyboardInputEvent(
+          const KeyboardInput& aEvent,
+          nsEventStatus* aOutStatus,
+          KeyboardInput* aOutEvent,
+          ScrollableLayerGuid* aOutTargetGuid,
+          uint64_t* aOutInputBlockId) override;
+
+  mozilla::ipc::IPCResult
   RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
 
   mozilla::ipc::IPCResult
   RecvZoomToRect(
           const ScrollableLayerGuid& aGuid,
           const CSSRect& aRect,
           const uint32_t& aFlags) override;
 
--- a/gfx/layers/ipc/PAPZCTreeManager.ipdl
+++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl
@@ -33,16 +33,17 @@ using class mozilla::WidgetWheelEvent fr
 using class mozilla::InputData from "InputData.h";
 using class mozilla::MultiTouchInput from "InputData.h";
 using class mozilla::MouseInput from "InputData.h";
 using class mozilla::PanGestureInput from "InputData.h";
 using class mozilla::PinchGestureInput from "InputData.h";
 using mozilla::PinchGestureInput::PinchGestureType from "InputData.h";
 using class mozilla::TapGestureInput from "InputData.h";
 using class mozilla::ScrollWheelInput from "InputData.h";
+using class mozilla::KeyboardInput from "InputData.h";
 
 namespace mozilla {
 namespace layers {
 
 /**
  * PAPZCTreeManager is a protocol for remoting an IAPZCTreeManager. PAPZCTreeManager
  * lives on the PCompositorBridge protocol which either connects to the compositor
  * thread in the main process, or to the compositor thread in the gpu processs.
@@ -116,16 +117,22 @@ parent:
              uint64_t            aOutInputBlockId);
 
   sync ReceiveScrollWheelInputEvent(ScrollWheelInput aEvent)
     returns (nsEventStatus       aOutStatus,
              ScrollWheelInput    aOutEvent,
              ScrollableLayerGuid aOutTargetGuid,
              uint64_t            aOutInputBlockId);
 
+  sync ReceiveKeyboardInputEvent(KeyboardInput aEvent)
+    returns (nsEventStatus       aOutStatus,
+             KeyboardInput       aOutEvent,
+             ScrollableLayerGuid aOutTargetGuid,
+             uint64_t            aOutInputBlockId);
+
   async UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage);
 
   sync ProcessUnhandledEvent(LayoutDeviceIntPoint aRefPoint)
     returns (LayoutDeviceIntPoint   aOutRefPoint,
              ScrollableLayerGuid    aOutTargetGuid,
              uint64_t               aOutFocusSequenceNumber);
 
   async __delete__();
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -274,16 +274,18 @@ private:
       return this->mValue == Default();
     }
   };
 
   // This is where DECL_GFX_PREF for each of the preferences should go.
   // We will keep these in an alphabetical order to make it easier to see if
   // a method accessing a pref already exists. Just add yours in the list.
 
+  DECL_GFX_PREF(Live, "accessibility.browsewithcaret", AccessibilityBrowseWithCaret, bool, false);
+
   // The apz prefs are explained in AsyncPanZoomController.cpp
   DECL_GFX_PREF(Live, "apz.allow_checkerboarding",             APZAllowCheckerboarding, bool, true);
   DECL_GFX_PREF(Live, "apz.allow_immediate_handoff",           APZAllowImmediateHandoff, bool, true);
   DECL_GFX_PREF(Live, "apz.allow_zooming",                     APZAllowZooming, bool, false);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_angle",          APZAxisBreakoutAngle, float, float(M_PI / 8.0) /* 22.5 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_threshold",      APZAxisBreakoutThreshold, float, 1.0f / 32.0f);
   DECL_GFX_PREF(Live, "apz.axis_lock.direct_pan_angle",        APZAllowedDirectPanAngle, float, float(M_PI / 3.0) /* 60 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.lock_angle",              APZAxisLockAngle, float, float(M_PI / 6.0) /* 30 degrees */);
@@ -305,16 +307,17 @@ private:
   DECL_GFX_PREF(Once, "apz.fling_curve_function_y1",           APZCurveFunctionY1, float, 0.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_y2",           APZCurveFunctionY2, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.fling_curve_threshold_inches_per_ms", APZCurveThreshold, float, -1.0f);
   DECL_GFX_PREF(Live, "apz.fling_friction",                    APZFlingFriction, float, 0.002f);
   DECL_GFX_PREF(Live, "apz.fling_min_velocity_threshold",      APZFlingMinVelocityThreshold, float, 0.5f);
   DECL_GFX_PREF(Live, "apz.fling_stop_on_tap_threshold",       APZFlingStopOnTapThreshold, float, 0.05f);
   DECL_GFX_PREF(Live, "apz.fling_stopped_threshold",           APZFlingStoppedThreshold, float, 0.01f);
   DECL_GFX_PREF(Live, "apz.highlight_checkerboarded_areas",    APZHighlightCheckerboardedAreas, bool, false);
+  DECL_GFX_PREF(Once, "apz.keyboard.enabled",                  APZKeyboardEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.max_velocity_inches_per_ms",        APZMaxVelocity, float, -1.0f);
   DECL_GFX_PREF(Once, "apz.max_velocity_queue_size",           APZMaxVelocityQueueSize, uint32_t, 5);
   DECL_GFX_PREF(Live, "apz.min_skate_speed",                   APZMinSkateSpeed, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.minimap.enabled",                   APZMinimap, bool, false);
   DECL_GFX_PREF(Live, "apz.minimap.visibility.enabled",        APZMinimapVisibilityEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.overscroll.enabled",                APZOverscrollEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.overscroll.min_pan_distance_ratio", APZMinPanDistanceRatio, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.overscroll.spring_friction",        APZOverscrollSpringFriction, float, 0.015f);
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -960,16 +960,18 @@ description =
 [PAPZCTreeManager::ReceivePinchGestureInputEvent]
 description =
 [PAPZCTreeManager::ReceiveTapGestureInputEvent]
 description =
 [PAPZCTreeManager::ReceiveScrollWheelInputEvent]
 description =
 [PAPZCTreeManager::ProcessUnhandledEvent]
 description =
+[PAPZCTreeManager::ReceiveKeyboardInputEvent]
+description =
 [PCompositorBridge::Initialize]
 description =
 [PCompositorBridge::GetFrameUniformity]
 description =
 [PCompositorBridge::WillClose]
 description =
 [PCompositorBridge::Pause]
 description =
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -691,16 +691,17 @@ pref("apz.fling_curve_function_y1", "0.0
 pref("apz.fling_curve_function_x2", "1.0");
 pref("apz.fling_curve_function_y2", "1.0");
 pref("apz.fling_curve_threshold_inches_per_ms", "-1.0");
 pref("apz.fling_friction", "0.002");
 pref("apz.fling_min_velocity_threshold", "0.5");
 pref("apz.fling_stop_on_tap_threshold", "0.05");
 pref("apz.fling_stopped_threshold", "0.01");
 pref("apz.highlight_checkerboarded_areas", false);
+pref("apz.keyboard.enabled", false);
 pref("apz.max_velocity_inches_per_ms", "-1.0");
 pref("apz.max_velocity_queue_size", 5);
 pref("apz.min_skate_speed", "1.0");
 pref("apz.minimap.enabled", false);
 pref("apz.minimap.visibility.enabled", false);
 pref("apz.overscroll.enabled", false);
 pref("apz.overscroll.min_pan_distance_ratio", "1.0");
 pref("apz.overscroll.spring_friction", "0.015");