Bug 1351783 part 12 - Create and sync focus sequence numbers. r=kats,botond,dvander
authorRyan Hunt <rhunt@eqrion.net>
Mon, 05 Jun 2017 19:45:31 -0500
changeset 366287 70136b7cb54b75cd105f7d6e6a92d9dd0240162f
parent 366286 36bfbfdfa4a187ceea3aa8013963f0153b0317f3
child 366288 c4a493398777eac3d29ea203371e88e6c6b9d58e
push id32099
push usercbook@mozilla.com
push dateWed, 28 Jun 2017 11:23:49 +0000
treeherdermozilla-central@306d2070e105 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats, botond, dvander
bugs1351783
milestone56.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 1351783 part 12 - Create and sync focus sequence numbers. r=kats,botond,dvander Focus can change at any moment in a document. This causes non-determinism and correctness problems for doing keyboard apz scrolling. To get around this, we will maintain deterministic behavior for focus changes initiated by input events and see if we can get away with more non-determinism for things like `setTimeout` In order to do this, we disable async keyboard scrolling when an input event is processed that could have a event listener. We then attach a sequence number to that input event and dispatch it to content. In content, we record the highest sequence number that we have processed from an event, and send that on each focus update. Using this, we can determine in APZ if we have a current focus target or if we are still waiting for an input event to be processed and focus to be reconfirmed. MozReview-Commit-ID: CWcu8YEFQz4
gfx/layers/apz/public/IAPZCTreeManager.cpp
gfx/layers/apz/public/IAPZCTreeManager.h
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/FocusState.cpp
gfx/layers/apz/src/FocusState.h
gfx/layers/apz/src/FocusTarget.cpp
gfx/layers/apz/src/FocusTarget.h
gfx/layers/ipc/APZCTreeManagerChild.cpp
gfx/layers/ipc/APZCTreeManagerChild.h
gfx/layers/ipc/APZCTreeManagerParent.cpp
gfx/layers/ipc/APZCTreeManagerParent.h
gfx/layers/ipc/LayersMessageUtils.h
gfx/layers/ipc/PAPZCTreeManager.ipdl
ipc/ipdl/sync-messages.ini
layout/base/PresShell.cpp
layout/base/nsIPresShell.h
widget/BasicEvents.h
widget/InputData.cpp
widget/InputData.h
widget/nsGUIEventIPC.h
--- a/gfx/layers/apz/public/IAPZCTreeManager.cpp
+++ b/gfx/layers/apz/public/IAPZCTreeManager.cpp
@@ -64,21 +64,22 @@ IAPZCTreeManager::ReceiveInputEvent(
         MouseInput input(mouseEvent);
         input.mOrigin = ScreenPoint(mouseEvent.mRefPoint.x, mouseEvent.mRefPoint.y);
 
         nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
 
         mouseEvent.mRefPoint.x = input.mOrigin.x;
         mouseEvent.mRefPoint.y = input.mOrigin.y;
         mouseEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
+        mouseEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
         return status;
 
       }
 
-      TransformEventRefPoint(&mouseEvent.mRefPoint, aOutTargetGuid);
+      ProcessUnhandledEvent(&mouseEvent.mRefPoint, aOutTargetGuid, &aEvent.mFocusSequenceNumber);
       return nsEventStatus_eIgnore;
     }
     case eTouchEventClass: {
 
       WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
       MultiTouchInput touchInput(touchEvent);
       nsEventStatus result = ReceiveInputEvent(touchInput, aOutTargetGuid, aOutInputBlockId);
       // touchInput was modified in-place to possibly remove some
@@ -87,16 +88,17 @@ IAPZCTreeManager::ReceiveInputEvent(
       // back into the WidgetInputEvent.
       touchEvent.mTouches.Clear();
       touchEvent.mTouches.SetCapacity(touchInput.mTouches.Length());
       for (size_t i = 0; i < touchInput.mTouches.Length(); i++) {
         *touchEvent.mTouches.AppendElement() =
           touchInput.mTouches[i].ToNewDOMTouch();
       }
       touchEvent.mFlags.mHandledByAPZ = touchInput.mHandledByAPZ;
+      touchEvent.mFocusSequenceNumber = touchInput.mFocusSequenceNumber;
       return result;
 
     }
     case eWheelEventClass: {
       WidgetWheelEvent& wheelEvent = *aEvent.AsWheelEvent();
 
       if (WillHandleWheelEvent(&wheelEvent)) {
 
@@ -128,28 +130,29 @@ IAPZCTreeManager::ReceiveInputEvent(
         EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
           &input.mUserDeltaMultiplierX,
           &input.mUserDeltaMultiplierY);
 
         nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
         wheelEvent.mRefPoint.x = input.mOrigin.x;
         wheelEvent.mRefPoint.y = input.mOrigin.y;
         wheelEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
+        wheelEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
         return status;
       }
 
       UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
-      TransformEventRefPoint(&aEvent.mRefPoint, aOutTargetGuid);
+      ProcessUnhandledEvent(&aEvent.mRefPoint, aOutTargetGuid, &aEvent.mFocusSequenceNumber);
       return nsEventStatus_eIgnore;
 
     }
     default: {
 
       UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
-      TransformEventRefPoint(&aEvent.mRefPoint, aOutTargetGuid);
+      ProcessUnhandledEvent(&aEvent.mRefPoint, aOutTargetGuid, &aEvent.mFocusSequenceNumber);
       return nsEventStatus_eIgnore;
 
     }
   }
 
   MOZ_ASSERT_UNREACHABLE("Invalid WidgetInputEvent type.");
   return nsEventStatus_eConsumeNoDefault;
 }
--- a/gfx/layers/apz/public/IAPZCTreeManager.h
+++ b/gfx/layers/apz/public/IAPZCTreeManager.h
@@ -206,19 +206,20 @@ public:
   // Even if this returns false, all wheel events in APZ-aware widgets must
   // be sent through APZ so they are transformed correctly for TabParent.
   static bool WillHandleWheelEvent(WidgetWheelEvent* aEvent);
 
 protected:
 
   // Methods to help process WidgetInputEvents (or manage conversion to/from InputData)
 
-  virtual void TransformEventRefPoint(
+  virtual void ProcessUnhandledEvent(
       LayoutDeviceIntPoint* aRefPoint,
-      ScrollableLayerGuid* aOutTargetGuid) = 0;
+      ScrollableLayerGuid* aOutTargetGuid,
+      uint64_t* aOutFocusSequenceNumber) = 0;
 
   virtual void UpdateWheelTransaction(
       LayoutDeviceIntPoint aRefPoint,
       EventMessage aEventMessage) = 0;
 
   // Discourage destruction outside of decref
 
   virtual ~IAPZCTreeManager() { }
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -160,16 +160,45 @@ APZCTreeManager::CheckerboardFlushObserv
     nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
     if (obsSvc) {
       obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard:Done", nullptr);
     }
   }
   return NS_OK;
 }
 
+/**
+ * A RAII class used for setting the focus sequence number on input events
+ * as they are being processed. Any input event is assumed to be potentially
+ * focus changing unless explicitly marked otherwise.
+ */
+class MOZ_RAII AutoFocusSequenceNumberSetter
+{
+public:
+  AutoFocusSequenceNumberSetter(FocusState& aFocusState, InputData& aEvent)
+    : mFocusState(aFocusState)
+    , mEvent(aEvent)
+    , mMayChangeFocus(true)
+  { }
+
+  void MarkAsNonFocusChanging() { mMayChangeFocus = false; }
+
+  ~AutoFocusSequenceNumberSetter()
+  {
+    if (mMayChangeFocus) {
+      mFocusState.ReceiveFocusChangingEvent();
+    }
+    mEvent.mFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
+  }
+
+private:
+  FocusState& mFocusState;
+  InputData& mEvent;
+  bool mMayChangeFocus;
+};
 
 /*static*/ const ScreenMargin
 APZCTreeManager::CalculatePendingDisplayPort(
   const FrameMetrics& aFrameMetrics,
   const ParentLayerPoint& aVelocity)
 {
   return AsyncPanZoomController::CalculatePendingDisplayPort(
     aFrameMetrics, aVelocity);
@@ -276,16 +305,18 @@ APZCTreeManager::UpdateHitTestingTreeImp
   if (aRoot) {
     std::stack<gfx::TreeAutoIndent> indents;
     std::stack<gfx::Matrix4x4> ancestorTransforms;
     HitTestingTreeNode* parent = nullptr;
     HitTestingTreeNode* next = nullptr;
     uint64_t layersId = aRootLayerTreeId;
     ancestorTransforms.push(Matrix4x4());
 
+    state.mLayersIdsToDestroy.erase(aRootLayerTreeId);
+
     mApzcTreeLog << "[start]\n";
     mTreeLock.AssertCurrentThreadOwns();
 
     ForEachNode<ReverseIterator>(aRoot,
         [&](ScrollNode aLayerMetrics)
         {
           mApzcTreeLog << aLayerMetrics.Name() << '\t';
 
@@ -293,19 +324,16 @@ APZCTreeManager::UpdateHitTestingTreeImp
                 aLayerMetrics.Metrics(), layersId, ancestorTransforms.top(),
                 parent, next, state);
           MOZ_ASSERT(node);
           AsyncPanZoomController* apzc = node->GetApzc();
           aLayerMetrics.SetApzc(apzc);
 
           mApzcTreeLog << '\n';
 
-          // Mark that this layer tree is being used
-          state.mLayersIdsToDestroy.erase(layersId);
-
           // Accumulate the CSS transform between layers that have an APZC.
           // In the terminology of the big comment above APZCTreeManager::GetScreenToApzcTransform, if
           // we are at layer M, then aAncestorTransform is NC * OC * PC, and we left-multiply MC and
           // compute ancestorTransform to be MC * NC * OC * PC. This gets passed down as the ancestor
           // transform to layer L when we recurse into the children below. If we are at a layer
           // with an APZC, such as P, then we reset the ancestorTransform to just PC, to start
           // the new accumulation as we go down.
           // If a transform is a perspective transform, it's ignored for this purpose
@@ -316,17 +344,25 @@ APZCTreeManager::UpdateHitTestingTreeImp
           }
           ancestorTransforms.push(currentTransform);
 
           // Note that |node| at this point will not have any children, otherwise we
           // we would have to set next to node->GetFirstChild().
           MOZ_ASSERT(!node->GetFirstChild());
           parent = node;
           next = nullptr;
-          layersId = aLayerMetrics.GetReferentId().valueOr(layersId);
+
+          // Update the layersId if we have a new one
+          if (Maybe<uint64_t> newLayersId = aLayerMetrics.GetReferentId()) {
+            layersId = *newLayersId;
+
+            // Mark that this layer tree is being used
+            state.mLayersIdsToDestroy.erase(layersId);
+          }
+
           indents.push(gfx::TreeAutoIndent(mApzcTreeLog));
         },
         [&](ScrollNode aLayerMetrics)
         {
           next = parent;
           parent = parent->GetParent();
           layersId = next->GetLayersId();
           ancestorTransforms.pop();
@@ -889,16 +925,19 @@ APZCTreeManager::FlushApzRepaints(uint64
 
 nsEventStatus
 APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
                                    ScrollableLayerGuid* aOutTargetGuid,
                                    uint64_t* aOutInputBlockId)
 {
   APZThreadUtils::AssertOnControllerThread();
 
+  // Use a RAII class for updating the focus sequence number of this event
+  AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent);
+
 #if defined(MOZ_WIDGET_ANDROID)
   MOZ_ASSERT(mToolbarAnimator);
   ScreenPoint scrollOffset;
   {
     MutexAutoLock lock(mTreeLock);
     RefPtr<AsyncPanZoomController> apzc = FindRootContentOrRootApzc();
     if (apzc) {
       scrollOffset = ViewAs<ScreenPixel>(apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL),
@@ -1056,17 +1095,16 @@ APZCTreeManager::ReceiveInputEvent(Input
           aOutTargetGuid->mScrollId = FrameMetrics::NULL_SCROLL_ID;
         }
       }
       break;
     } case SCROLLWHEEL_INPUT: {
       FlushRepaintsToClearScreenToGeckoTransform();
 
       ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput();
-
       wheelInput.mHandledByAPZ = WillHandleInput(wheelInput);
       if (!wheelInput.mHandledByAPZ) {
         return result;
       }
 
       RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(wheelInput.mOrigin,
                                                             &hitResult);
       if (apzc) {
@@ -1425,18 +1463,19 @@ APZCTreeManager::UpdateWheelTransaction(
      txn->EndTransaction();
      return;
    default:
      break;
   }
 }
 
 void
-APZCTreeManager::TransformEventRefPoint(LayoutDeviceIntPoint* aRefPoint,
-                              ScrollableLayerGuid* aOutTargetGuid)
+APZCTreeManager::ProcessUnhandledEvent(LayoutDeviceIntPoint* aRefPoint,
+                                        ScrollableLayerGuid*  aOutTargetGuid,
+                                        uint64_t*             aOutFocusSequenceNumber)
 {
   // Transform the aRefPoint.
   // If the event hits an overscrolled APZC, instruct the caller to ignore it.
   HitTestResult hitResult = HitNothing;
   PixelCastJustification LDIsScreen = PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent;
   ScreenIntPoint refPointAsScreen =
     ViewAs<ScreenPixel>(*aRefPoint, LDIsScreen);
   RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(refPointAsScreen, &hitResult);
@@ -1448,16 +1487,20 @@ APZCTreeManager::TransformEventRefPoint(
     ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
     Maybe<ScreenIntPoint> untransformedRefPoint =
       UntransformBy(outTransform, refPointAsScreen);
     if (untransformedRefPoint) {
       *aRefPoint =
         ViewAs<LayoutDevicePixel>(*untransformedRefPoint, LDIsScreen);
     }
   }
+
+  // Update the focus sequence number and attach it to the event
+  mFocusState.ReceiveFocusChangingEvent();
+  *aOutFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
 }
 
 void
 APZCTreeManager::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
 {
   if (mApzcForInputBlock) {
     mApzcForInputBlock->HandleTouchVelocity(aTimestampMs, aSpeedY);
   }
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -446,19 +446,20 @@ public:
    *
    * On slow running tests, drags and touch events can be misinterpreted
    * as a long tap. This allows tests to disable long tap gesture detection.
    */
   void SetLongTapEnabled(bool aTapGestureEnabled) override;
 
   // Methods to help process WidgetInputEvents (or manage conversion to/from InputData)
 
-  void TransformEventRefPoint(
+  void ProcessUnhandledEvent(
       LayoutDeviceIntPoint* aRefPoint,
-      ScrollableLayerGuid* aOutTargetGuid) override;
+      ScrollableLayerGuid*  aOutTargetGuid,
+      uint64_t*             aOutFocusSequenceNumber) override;
 
   void UpdateWheelTransaction(
       LayoutDeviceIntPoint aRefPoint,
       EventMessage aEventMessage) override;
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~APZCTreeManager();
--- a/gfx/layers/apz/src/FocusState.cpp
+++ b/gfx/layers/apz/src/FocusState.cpp
@@ -4,23 +4,38 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/FocusState.h"
 
 namespace mozilla {
 namespace layers {
 
 FocusState::FocusState()
-  : mFocusHasKeyEventListeners(false)
+  : mLastAPZProcessedEvent(1)
+  , mLastContentProcessedEvent(0)
+  , mFocusHasKeyEventListeners(false)
   , mFocusLayersId(0)
   , mFocusHorizontalTarget(FrameMetrics::NULL_SCROLL_ID)
   , mFocusVerticalTarget(FrameMetrics::NULL_SCROLL_ID)
 {
 }
 
+bool
+FocusState::IsCurrent() const
+{
+  MOZ_ASSERT(mLastContentProcessedEvent <= mLastAPZProcessedEvent);
+  return mLastContentProcessedEvent == mLastAPZProcessedEvent;
+}
+
+void
+FocusState::ReceiveFocusChangingEvent()
+{
+  mLastAPZProcessedEvent += 1;
+}
+
 void
 FocusState::Update(uint64_t aRootLayerTreeId,
                    uint64_t aOriginatingLayersId,
                    const FocusTarget& aState)
 {
   // Update the focus tree with the latest target
   mFocusTree[aOriginatingLayersId] = aState;
 
@@ -55,35 +70,42 @@ FocusState::Update(uint64_t aRootLayerTr
         // The focus target is in a child layer tree
         mFocusLayersId = target.mData.mRefLayerId;
         break;
       }
       case FocusTarget::eScrollLayer: {
         // This is the global focus target
         mFocusHorizontalTarget = target.mData.mScrollTargets.mHorizontal;
         mFocusVerticalTarget = target.mData.mScrollTargets.mVertical;
+
+        // Mark what sequence number this target has so we can determine whether
+        // it is stale or not
+        mLastContentProcessedEvent = target.mSequenceNumber;
         return;
       }
       case FocusTarget::eNone: {
+        // Mark what sequence number this target has for debugging purposes so
+        // we can always accurately report on whether we are stale or not
+        mLastContentProcessedEvent = target.mSequenceNumber;
         return;
       }
       case FocusTarget::eSentinel: {
         MOZ_ASSERT_UNREACHABLE("Invalid FocusTargetType");
       }
     }
   }
 }
 
 std::unordered_set<uint64_t>
 FocusState::GetFocusTargetLayerIds() const
 {
   std::unordered_set<uint64_t> layersIds;
   layersIds.reserve(mFocusTree.size());
 
-  for (auto focusNode : mFocusTree) {
+  for (const auto& focusNode : mFocusTree) {
     layersIds.insert(focusNode.first);
   }
 
   return layersIds;
 }
 
 void
 FocusState::RemoveFocusTarget(uint64_t aLayersId)
--- a/gfx/layers/apz/src/FocusState.h
+++ b/gfx/layers/apz/src/FocusState.h
@@ -5,16 +5,17 @@
 
 #ifndef mozilla_layers_FocusState_h
 #define mozilla_layers_FocusState_h
 
 #include <unordered_map>    // for std::unordered_map
 #include <unordered_set>    // for std::unordered_set
 
 #include "FrameMetrics.h"   // for FrameMetrics::ViewID
+
 #include "mozilla/layers/FocusTarget.h" // for FocusTarget
 
 namespace mozilla {
 namespace layers {
 
 /**
  * This class is used for tracking chrome and content focus targets and calculating
  * global focus information from them for use by APZCTreeManager for async keyboard
@@ -65,16 +66,38 @@ namespace layers {
  * again.
  */
 class FocusState final
 {
 public:
   FocusState();
 
   /**
+   * The sequence number of the last potentially focus changing event processed
+   * by APZ. This number starts at one and increases monotonically. This number
+   * will never be zero as that is used to catch uninitialized focus sequence
+   * numbers on input events.
+   */
+  uint64_t LastAPZProcessedEvent() const { return mLastAPZProcessedEvent; }
+
+  /**
+   * Whether the current focus state is known to be current or else if an event
+   * has been processed that could change the focus but we have not received an
+   * update with a new confirmed target.
+   */
+  bool IsCurrent() const;
+
+  /**
+   * Notify focus state of a potentially focus changing event. This will
+   * increment the current focus sequence number. The new value can be gotten
+   * from LastAPZProcessedEvent().
+   */
+  void ReceiveFocusChangingEvent();
+
+  /**
    * Update the internal focus tree and recalculate the global focus target for
    * a focus target update received from chrome or content.
    *
    * @param aRootLayerTreeId the layer tree ID of the root layer for the
                              parent APZCTreeManager
    * @param aOriginatingLayersId the layer tree ID that this focus target
                                  belongs to
    */
@@ -91,16 +114,26 @@ public:
    * Removes a focus target by its layer tree ID.
    */
   void RemoveFocusTarget(uint64_t aLayersId);
 
 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
+  // never be zero as that is used to catch uninitialized focus sequence numbers
+  // on input events.
+  uint64_t mLastAPZProcessedEvent;
+  // The focus sequence number last received in a focus update.
+  uint64_t mLastContentProcessedEvent;
+
   // A flag whether there is a key listener on the event target chain for the
   // focused element
   bool mFocusHasKeyEventListeners;
 
   // The layer tree ID which contains the scrollable frame of the focused element
   uint64_t mFocusLayersId;
   // The scrollable layer corresponding to the scrollable frame that is used to
   // scroll the focused element. This depends on the direction the user is
--- a/gfx/layers/apz/src/FocusTarget.cpp
+++ b/gfx/layers/apz/src/FocusTarget.cpp
@@ -62,23 +62,26 @@ HasListenersForKeyEvents(nsIContent* aCo
 
 static bool
 IsEditableNode(nsINode* aNode)
 {
   return aNode && aNode->IsEditable();
 }
 
 FocusTarget::FocusTarget()
-  : mFocusHasKeyEventListeners(false)
+  : mSequenceNumber(0)
+  , mFocusHasKeyEventListeners(false)
   , mType(FocusTarget::eNone)
 {
 }
 
-FocusTarget::FocusTarget(nsIPresShell* aRootPresShell)
-  : mFocusHasKeyEventListeners(false)
+FocusTarget::FocusTarget(nsIPresShell* aRootPresShell,
+                         uint64_t aFocusSequenceNumber)
+  : mSequenceNumber(aFocusSequenceNumber)
+  , mFocusHasKeyEventListeners(false)
 {
   MOZ_ASSERT(aRootPresShell);
   MOZ_ASSERT(NS_IsMainThread());
 
   // Key events can be retargeted to a child PresShell when there is an iframe
   nsCOMPtr<nsIPresShell> presShell = GetRetargetEventPresShell(aRootPresShell);
 
   // Get the content that should be scrolled for this PresShell, which is
@@ -127,17 +130,18 @@ FocusTarget::FocusTarget(nsIPresShell* a
     nsLayoutUtils::FindIDForScrollableFrame(horizontal);
   mData.mScrollTargets.mVertical =
     nsLayoutUtils::FindIDForScrollableFrame(vertical);
 }
 
 bool
 FocusTarget::operator==(const FocusTarget& aRhs) const
 {
-  if (mFocusHasKeyEventListeners != aRhs.mFocusHasKeyEventListeners ||
+  if (mSequenceNumber != aRhs.mSequenceNumber ||
+      mFocusHasKeyEventListeners != aRhs.mFocusHasKeyEventListeners ||
       mType != aRhs.mType) {
     return false;
   }
 
   if (mType == FocusTarget::eRefLayer) {
       return mData.mRefLayerId == aRhs.mData.mRefLayerId;
   } else if (mType == FocusTarget::eScrollLayer) {
       return mData.mScrollTargets.mHorizontal == aRhs.mData.mScrollTargets.mHorizontal &&
--- a/gfx/layers/apz/src/FocusTarget.h
+++ b/gfx/layers/apz/src/FocusTarget.h
@@ -45,21 +45,25 @@ public:
     ScrollTargets mScrollTargets;
   };
 
   FocusTarget();
 
   /**
    * Construct a focus target for the specified top level PresShell
    */
-  FocusTarget(nsIPresShell* aRootPresShell);
+  FocusTarget(nsIPresShell* aRootPresShell,
+              uint64_t aFocusSequenceNumber);
 
   bool operator==(const FocusTarget& aRhs) const;
 
 public:
+  // The content sequence number recorded at the time of this class's creation
+  uint64_t mSequenceNumber;
+
   // Whether there are keydown, keypress, or keyup event listeners
   // in the event target chain of the focused element
   bool mFocusHasKeyEventListeners;
 
   FocusTargetType mType;
   FocusTargetData mData;
 };
 
--- a/gfx/layers/ipc/APZCTreeManagerChild.cpp
+++ b/gfx/layers/ipc/APZCTreeManagerChild.cpp
@@ -208,21 +208,25 @@ APZCTreeManagerChild::ProcessTouchVeloci
 void
 APZCTreeManagerChild::UpdateWheelTransaction(
     LayoutDeviceIntPoint aRefPoint,
     EventMessage aEventMessage)
 {
   SendUpdateWheelTransaction(aRefPoint, aEventMessage);
 }
 
-void APZCTreeManagerChild::TransformEventRefPoint(
+void APZCTreeManagerChild::ProcessUnhandledEvent(
     LayoutDeviceIntPoint* aRefPoint,
-    ScrollableLayerGuid* aOutTargetGuid)
+    ScrollableLayerGuid*  aOutTargetGuid,
+    uint64_t*             aOutFocusSequenceNumber)
 {
-  SendTransformEventRefPoint(*aRefPoint, aRefPoint, aOutTargetGuid);
+  SendProcessUnhandledEvent(*aRefPoint,
+                            aRefPoint,
+                            aOutTargetGuid,
+                            aOutFocusSequenceNumber);
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerChild::RecvHandleTap(const TapType& aType,
                                     const LayoutDevicePoint& aPoint,
                                     const Modifiers& aModifiers,
                                     const ScrollableLayerGuid& aGuid,
                                     const uint64_t& aInputBlockId)
--- a/gfx/layers/ipc/APZCTreeManagerChild.h
+++ b/gfx/layers/ipc/APZCTreeManagerChild.h
@@ -72,19 +72,20 @@ public:
 
   void
   SetLongTapEnabled(bool aTapGestureEnabled) override;
 
   void
   ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) override;
 
   void
-  TransformEventRefPoint(
+  ProcessUnhandledEvent(
           LayoutDeviceIntPoint* aRefPoint,
-          ScrollableLayerGuid* aOutTargetGuid) override;
+          ScrollableLayerGuid*  aOutTargetGuid,
+          uint64_t*             aOutFocusSequenceNumber) override;
 
   void
   UpdateWheelTransaction(
           LayoutDeviceIntPoint aRefPoint,
           EventMessage aEventMessage) override;
 
 protected:
   mozilla::ipc::IPCResult RecvHandleTap(const TapType& aType,
--- a/gfx/layers/ipc/APZCTreeManagerParent.cpp
+++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp
@@ -302,22 +302,23 @@ APZCTreeManagerParent::RecvUpdateWheelTr
         const LayoutDeviceIntPoint& aRefPoint,
         const EventMessage& aEventMessage)
 {
   mTreeManager->UpdateWheelTransaction(aRefPoint, aEventMessage);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-APZCTreeManagerParent::RecvTransformEventRefPoint(
+APZCTreeManagerParent::RecvProcessUnhandledEvent(
         const LayoutDeviceIntPoint& aRefPoint,
         LayoutDeviceIntPoint* aOutRefPoint,
-        ScrollableLayerGuid* aOutTargetGuid)
+        ScrollableLayerGuid*  aOutTargetGuid,
+        uint64_t*             aOutFocusSequenceNumber)
 {
   LayoutDeviceIntPoint refPoint = aRefPoint;
-  mTreeManager->TransformEventRefPoint(&refPoint, aOutTargetGuid);
+  mTreeManager->ProcessUnhandledEvent(&refPoint, aOutTargetGuid, aOutFocusSequenceNumber);
   *aOutRefPoint = refPoint;
 
   return IPC_OK();
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/APZCTreeManagerParent.h
+++ b/gfx/layers/ipc/APZCTreeManagerParent.h
@@ -127,20 +127,21 @@ public:
           const float& aSpeedY) override;
 
   mozilla::ipc::IPCResult
   RecvUpdateWheelTransaction(
           const LayoutDeviceIntPoint& aRefPoint,
           const EventMessage& aEventMessage) override;
 
   mozilla::ipc::IPCResult
-  RecvTransformEventRefPoint(
+  RecvProcessUnhandledEvent(
           const LayoutDeviceIntPoint& aRefPoint,
           LayoutDeviceIntPoint* aOutRefPoint,
-          ScrollableLayerGuid* aOutTargetGuid) override;
+          ScrollableLayerGuid*  aOutTargetGuid,
+          uint64_t*             aOutFocusSequenceNumber) override;
 
   void
   ActorDestroy(ActorDestroyReason aWhy) override { }
 
 private:
   uint64_t mLayersId;
   RefPtr<APZCTreeManager> mTreeManager;
 };
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -449,28 +449,30 @@ struct ParamTraits<mozilla::layers::Focu
 
 template <>
 struct ParamTraits<mozilla::layers::FocusTarget>
 {
   typedef mozilla::layers::FocusTarget paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
+    WriteParam(aMsg, aParam.mSequenceNumber);
     WriteParam(aMsg, aParam.mFocusHasKeyEventListeners);
     WriteParam(aMsg, aParam.mType);
     if (aParam.mType == mozilla::layers::FocusTarget::eRefLayer) {
       WriteParam(aMsg, aParam.mData.mRefLayerId);
     } else if (aParam.mType == mozilla::layers::FocusTarget::eScrollLayer) {
       WriteParam(aMsg, aParam.mData.mScrollTargets);
     }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &aResult->mFocusHasKeyEventListeners) ||
+    if (!ReadParam(aMsg, aIter, &aResult->mSequenceNumber) ||
+        !ReadParam(aMsg, aIter, &aResult->mFocusHasKeyEventListeners) ||
         !ReadParam(aMsg, aIter, &aResult->mType)) {
       return false;
     }
 
     if (aResult->mType == mozilla::layers::FocusTarget::eRefLayer) {
       return ReadParam(aMsg, aIter, &aResult->mData.mRefLayerId);
     } else if (aResult->mType == mozilla::layers::FocusTarget::eScrollLayer) {
       return ReadParam(aMsg, aIter, &aResult->mData.mScrollTargets);
--- a/gfx/layers/ipc/PAPZCTreeManager.ipdl
+++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl
@@ -118,19 +118,20 @@ parent:
   sync ReceiveScrollWheelInputEvent(ScrollWheelInput aEvent)
     returns (nsEventStatus       aOutStatus,
              ScrollWheelInput    aOutEvent,
              ScrollableLayerGuid aOutTargetGuid,
              uint64_t            aOutInputBlockId);
 
   async UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage);
 
-  sync TransformEventRefPoint(LayoutDeviceIntPoint aRefPoint)
+  sync ProcessUnhandledEvent(LayoutDeviceIntPoint aRefPoint)
     returns (LayoutDeviceIntPoint   aOutRefPoint,
-             ScrollableLayerGuid    aOutTargetGuid);
+             ScrollableLayerGuid    aOutTargetGuid,
+             uint64_t               aOutFocusSequenceNumber);
 
   async __delete__();
 
 child:
 
   async HandleTap(TapType aType, LayoutDevicePoint point, Modifiers aModifiers,
                   ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
 
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -962,17 +962,17 @@ description =
 [PAPZCTreeManager::ReceivePanGestureInputEvent]
 description =
 [PAPZCTreeManager::ReceivePinchGestureInputEvent]
 description =
 [PAPZCTreeManager::ReceiveTapGestureInputEvent]
 description =
 [PAPZCTreeManager::ReceiveScrollWheelInputEvent]
 description =
-[PAPZCTreeManager::TransformEventRefPoint]
+[PAPZCTreeManager::ProcessUnhandledEvent]
 description =
 [PCompositorBridge::Initialize]
 description =
 [PCompositorBridge::GetFrameUniformity]
 description =
 [PCompositorBridge::WillClose]
 description =
 [PCompositorBridge::Pause]
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -797,16 +797,17 @@ nsIPresShell::nsIPresShell()
     , mSuppressInterruptibleReflows(false)
     , mScrollPositionClampingScrollPortSizeSet(false)
     , mNeedLayoutFlush(true)
     , mNeedStyleFlush(true)
     , mObservingStyleFlushes(false)
     , mObservingLayoutFlushes(false)
     , mNeedThrottledAnimationFlush(true)
     , mPresShellId(0)
+    , mAPZFocusSequenceNumber(0)
     , mFontSizeInflationEmPerLine(0)
     , mFontSizeInflationMinTwips(0)
     , mFontSizeInflationLineThreshold(0)
     , mFontSizeInflationForceEnabled(false)
     , mFontSizeInflationDisabledInMasterProcess(false)
     , mFontSizeInflationEnabled(false)
     , mFontSizeInflationEnabledIsDirty(false)
     , mPaintingIsFrozen(false)
@@ -6335,17 +6336,17 @@ PresShell::Paint(nsView*         aViewTo
 
   if (!mIsActive) {
     return;
   }
 
   // Update the focus target for async keyboard scrolling. This will be forwarded
   // to APZ by nsDisplayList::PaintRoot. We need to to do this before we enter
   // the paint phase because dispatching eVoid events can cause layout to happen.
-  mAPZFocusTarget = FocusTarget(this);
+  mAPZFocusTarget = FocusTarget(this, mAPZFocusSequenceNumber);
 
   nsPresContext* presContext = GetPresContext();
   AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
 
   nsIFrame* frame = aViewToPaint->GetFrame();
 
   LayerManager* layerManager =
     aViewToPaint->GetWidget()->GetLayerManager();
@@ -7149,16 +7150,22 @@ PresShell::HandleEvent(nsIFrame* aFrame,
       type = SourceEventType::Key;
     }
     taskTracerEvent.emplace(type);
   }
 #endif
 
   NS_ASSERTION(aFrame, "aFrame should be not null");
 
+  // Update the latest focus sequence number with this new sequence number
+  if (mAPZFocusSequenceNumber < aEvent->mFocusSequenceNumber) {
+    // XXX should we push a new FocusTarget to APZ here
+    mAPZFocusSequenceNumber = aEvent->mFocusSequenceNumber;
+  }
+
   if (sPointerEventEnabled) {
     AutoWeakFrame weakFrame(aFrame);
     nsCOMPtr<nsIContent> targetContent;
     DispatchPointerFromMouseOrTouch(this, aFrame, aEvent, aDontRetargetEvents,
                                     aEventStatus,
                                     getter_AddRefs(targetContent));
     if (!weakFrame.IsAlive()) {
       if (targetContent) {
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1900,16 +1900,18 @@ protected:
   bool mObservingLayoutFlushes: 1;
 
   // True if there are throttled animations that would be processed when
   // performing a flush with mFlushAnimations == true.
   bool mNeedThrottledAnimationFlush : 1;
 
   uint32_t                  mPresShellId;
 
+  // The focus sequence number of the last processed input event
+  uint64_t                  mAPZFocusSequenceNumber;
   // The focus information needed for async keyboard scrolling
   FocusTarget               mAPZFocusTarget;
 
   static nsIContent*        gKeyDownTarget;
 
   // Cached font inflation values. This is done to prevent changing of font
   // inflation until a page is reloaded.
   uint32_t mFontSizeInflationEmPerLine;
--- a/widget/BasicEvents.h
+++ b/widget/BasicEvents.h
@@ -349,16 +349,17 @@ protected:
   WidgetEvent(bool aIsTrusted,
               EventMessage aMessage,
               EventClassID aEventClassID)
     : WidgetEventTime()
     , mClass(aEventClassID)
     , mMessage(aMessage)
     , mRefPoint(0, 0)
     , mLastRefPoint(0, 0)
+    , mFocusSequenceNumber(0)
     , mSpecifiedEventType(nullptr)
   {
     MOZ_COUNT_CTOR(WidgetEvent);
     mFlags.Clear();
     mFlags.mIsTrusted = aIsTrusted;
     SetDefaultCancelableAndBubbles();
     SetDefaultComposed();
     SetDefaultComposedInNativeAnonymousContent();
@@ -400,16 +401,20 @@ public:
 
   EventClassID mClass;
   EventMessage mMessage;
   // Relative to the widget of the event, or if there is no widget then it is
   // in screen coordinates. Not modified by layout code.
   LayoutDeviceIntPoint mRefPoint;
   // The previous mRefPoint, if known, used to calculate mouse movement deltas.
   LayoutDeviceIntPoint mLastRefPoint;
+  // The sequence number of the last potentially focus changing event handled
+  // by APZ. This is used to track when that event has been processed by content,
+  // and focus can be reconfirmed for async keyboard scrolling.
+  uint64_t mFocusSequenceNumber;
   // See BaseEventFlags definition for the detail.
   BaseEventFlags mFlags;
 
   // If JS creates an event with unknown event type or known event type but
   // for different event interface, the event type is stored to this.
   // NOTE: This is always used if the instance is a WidgetCommandEvent instance.
   nsCOMPtr<nsIAtom> mSpecifiedEventType;
 
@@ -430,16 +435,17 @@ public:
   dom::EventTarget* GetOriginalDOMEventTarget() const;
 
   void AssignEventData(const WidgetEvent& aEvent, bool aCopyTargets)
   {
     // mClass should be initialized with the constructor.
     // mMessage should be initialized with the constructor.
     mRefPoint = aEvent.mRefPoint;
     // mLastRefPoint doesn't need to be copied.
+    mFocusSequenceNumber = aEvent.mFocusSequenceNumber;
     AssignEventTime(aEvent);
     // mFlags should be copied manually if it's necessary.
     mSpecifiedEventType = aEvent.mSpecifiedEventType;
     // mSpecifiedEventTypeString should be copied manually if it's necessary.
     mTarget = aCopyTargets ? aEvent.mTarget : nullptr;
     mCurrentTarget = aCopyTargets ? aEvent.mCurrentTarget : nullptr;
     mOriginalTarget = aCopyTargets ? aEvent.mOriginalTarget : nullptr;
   }
--- a/widget/InputData.cpp
+++ b/widget/InputData.cpp
@@ -18,25 +18,27 @@ using namespace dom;
 
 InputData::~InputData()
 {
 }
 
 InputData::InputData(InputType aInputType)
   : mInputType(aInputType)
   , mTime(0)
+  , mFocusSequenceNumber(0)
   , modifiers(0)
 {
 }
 
 InputData::InputData(InputType aInputType, uint32_t aTime, TimeStamp aTimeStamp,
                      Modifiers aModifiers)
   : mInputType(aInputType)
   , mTime(aTime)
   , mTimeStamp(aTimeStamp)
+  , mFocusSequenceNumber(0)
   , modifiers(aModifiers)
 {
 }
 
 SingleTouchData::SingleTouchData(int32_t aIdentifier, ScreenIntPoint aScreenPoint,
                                  ScreenSize aRadius, float aRotationAngle,
                                  float aForce)
   : mIdentifier(aIdentifier)
@@ -221,16 +223,17 @@ MultiTouchInput::ToWidgetTouchEvent(nsIW
   if (touchEventMessage == eVoidEvent) {
     return event;
   }
 
   event.mModifiers = this->modifiers;
   event.mTime = this->mTime;
   event.mTimeStamp = this->mTimeStamp;
   event.mFlags.mHandledByAPZ = mHandledByAPZ;
+  event.mFocusSequenceNumber = mFocusSequenceNumber;
 
   for (size_t i = 0; i < mTouches.Length(); i++) {
     *event.mTouches.AppendElement() = mTouches[i].ToNewDOMTouch();
   }
 
   return event;
 }
 
@@ -264,16 +267,17 @@ MultiTouchInput::ToWidgetMouseEvent(nsIW
   event.mRefPoint.x = firstTouch.mScreenPoint.x;
   event.mRefPoint.y = firstTouch.mScreenPoint.y;
 
   event.mTime = mTime;
   event.button = WidgetMouseEvent::eLeftButton;
   event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
   event.mModifiers = modifiers;
   event.mFlags.mHandledByAPZ = mHandledByAPZ;
+  event.mFocusSequenceNumber = mFocusSequenceNumber;
 
   if (mouseEventMessage != eMouseMove) {
     event.mClickCount = 1;
   }
 
   return event;
 }
 
@@ -465,16 +469,17 @@ MouseInput::ToWidgetMouseEvent(nsIWidget
   event.mTimeStamp = mTimeStamp;
   event.mFlags.mHandledByAPZ = mHandledByAPZ;
   event.mRefPoint =
     RoundedToInt(ViewAs<LayoutDevicePixel>(mOrigin,
       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
   event.mClickCount = clickCount;
   event.inputSource = mInputSource;
   event.mIgnoreRootScrollFrame = true;
+  event.mFocusSequenceNumber = mFocusSequenceNumber;
 
   return event;
 }
 
 PanGestureInput::PanGestureInput()
   : InputData(PANGESTURE_INPUT)
   , mLineOrPageDeltaX(0)
   , mLineOrPageDeltaY(0)
@@ -532,16 +537,17 @@ PanGestureInput::ToWidgetWheelEvent(nsIW
   wheelEvent.mDeltaMode = nsIDOMWheelEvent::DOM_DELTA_PIXEL;
   wheelEvent.mMayHaveMomentum = true; // pan inputs may have momentum
   wheelEvent.mIsMomentum = IsMomentum();
   wheelEvent.mLineOrPageDeltaX = mLineOrPageDeltaX;
   wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
   wheelEvent.mDeltaX = mPanDisplacement.x;
   wheelEvent.mDeltaY = mPanDisplacement.y;
   wheelEvent.mFlags.mHandledByAPZ = mHandledByAPZ;
+  wheelEvent.mFocusSequenceNumber = mFocusSequenceNumber;
   return wheelEvent;
 }
 
 bool
 PanGestureInput::TransformToLocal(const ScreenToParentLayerMatrix4x4& aTransform)
 { 
   Maybe<ParentLayerPoint> panStartPoint = UntransformBy(aTransform, mPanStartPoint);
   if (!panStartPoint) {
@@ -759,16 +765,17 @@ ScrollWheelInput::ToWidgetWheelEvent(nsI
   wheelEvent.mIsMomentum = mIsMomentum;
   wheelEvent.mDeltaX = mDeltaX;
   wheelEvent.mDeltaY = mDeltaY;
   wheelEvent.mLineOrPageDeltaX = mLineOrPageDeltaX;
   wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
   wheelEvent.mAllowToOverrideSystemScrollSpeed =
     mAllowToOverrideSystemScrollSpeed;
   wheelEvent.mFlags.mHandledByAPZ = mHandledByAPZ;
+  wheelEvent.mFocusSequenceNumber = mFocusSequenceNumber;
   return wheelEvent;
 }
 
 bool
 ScrollWheelInput::TransformToLocal(const ScreenToParentLayerMatrix4x4& aTransform)
 {
   Maybe<ParentLayerPoint> point = UntransformBy(aTransform, mOrigin);
   if (!point) {
--- a/widget/InputData.h
+++ b/widget/InputData.h
@@ -81,16 +81,20 @@ public:
   // matters when this data is used as an event. We use uint32_t instead of
   // TimeStamp because it is easier to convert from WidgetInputEvent. The time
   // is platform-specific but it in the case of B2G and Fennec it is since
   // startup.
   uint32_t mTime;
   // Set in parallel to mTime until we determine it is safe to drop
   // platform-specific event times (see bug 77992).
   TimeStamp mTimeStamp;
+  // The sequence number of the last potentially focus changing event handled
+  // by APZ. This is used to track when that event has been processed by content,
+  // and focus can be reconfirmed for async keyboard scrolling.
+  uint64_t mFocusSequenceNumber;
 
   Modifiers modifiers;
 
   INPUTDATA_AS_CHILD_TYPE(MultiTouchInput, MULTITOUCH_INPUT)
   INPUTDATA_AS_CHILD_TYPE(MouseInput, MOUSE_INPUT)
   INPUTDATA_AS_CHILD_TYPE(PanGestureInput, PANGESTURE_INPUT)
   INPUTDATA_AS_CHILD_TYPE(PinchGestureInput, PINCHGESTURE_INPUT)
   INPUTDATA_AS_CHILD_TYPE(TapGestureInput, TAPGESTURE_INPUT)
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -60,27 +60,29 @@ struct ParamTraits<mozilla::WidgetEvent>
   typedef mozilla::WidgetEvent paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg,
       static_cast<mozilla::EventClassIDType>(aParam.mClass));
     WriteParam(aMsg, aParam.mMessage);
     WriteParam(aMsg, aParam.mRefPoint);
+    WriteParam(aMsg, aParam.mFocusSequenceNumber);
     WriteParam(aMsg, aParam.mTime);
     WriteParam(aMsg, aParam.mTimeStamp);
     WriteParam(aMsg, aParam.mFlags);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     mozilla::EventClassIDType eventClassID = 0;
     bool ret = ReadParam(aMsg, aIter, &eventClassID) &&
                ReadParam(aMsg, aIter, &aResult->mMessage) &&
                ReadParam(aMsg, aIter, &aResult->mRefPoint) &&
+               ReadParam(aMsg, aIter, &aResult->mFocusSequenceNumber) &&
                ReadParam(aMsg, aIter, &aResult->mTime) &&
                ReadParam(aMsg, aIter, &aResult->mTimeStamp) &&
                ReadParam(aMsg, aIter, &aResult->mFlags);
     aResult->mClass = static_cast<mozilla::EventClassID>(eventClassID);
     return ret;
   }
 };
 
@@ -1075,24 +1077,26 @@ struct ParamTraits<mozilla::InputData>
   typedef mozilla::InputData paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mInputType);
     WriteParam(aMsg, aParam.mTime);
     WriteParam(aMsg, aParam.mTimeStamp);
     WriteParam(aMsg, aParam.modifiers);
+    WriteParam(aMsg, aParam.mFocusSequenceNumber);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->mInputType) &&
            ReadParam(aMsg, aIter, &aResult->mTime) &&
            ReadParam(aMsg, aIter, &aResult->mTimeStamp) &&
-           ReadParam(aMsg, aIter, &aResult->modifiers);
+           ReadParam(aMsg, aIter, &aResult->modifiers) &&
+           ReadParam(aMsg, aIter, &aResult->mFocusSequenceNumber);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::SingleTouchData>
 {
   typedef mozilla::SingleTouchData paramType;