Bug 1083395 - Replace the touch block balance with an input block identifier. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 24 Oct 2014 13:29:30 -0400
changeset 212294 6efc9dfeae46e54dc706f2f42d36153e8c67fc0f
parent 212293 e194c411165c0835a0fbfb68767d9a2e3032b62f
child 212295 a5e6dce29017c3c95f9d4519b867743205759713
push idunknown
push userunknown
push dateunknown
reviewersbotond
bugs1083395
milestone36.0a1
Bug 1083395 - Replace the touch block balance with an input block identifier. r=botond
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
gfx/layers/apz/public/GeckoContentController.h
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.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/tests/gtest/TestAsyncPanZoomController.cpp
layout/ipc/RenderFrameParent.cpp
layout/ipc/RenderFrameParent.h
widget/android/APZCCallbackHandler.cpp
widget/android/APZCCallbackHandler.h
widget/android/AndroidJNI.cpp
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
widget/android/nsWindow.cpp
widget/cocoa/nsChildView.mm
widget/gonk/ParentProcessController.h
widget/windows/winrt/APZController.cpp
widget/windows/winrt/APZController.h
widget/windows/winrt/MetroInput.cpp
widget/windows/winrt/MetroInput.h
widget/windows/winrt/MetroWidget.cpp
widget/windows/winrt/MetroWidget.h
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -370,17 +370,17 @@ parent:
 
     /**
      * We know for sure that content has either preventDefaulted or not
      * preventDefaulted. This applies to an entire batch of touch events. It is
      * expected that, if there are any DOM touch listeners, touch events will be
      * batched and only processed for panning and zooming if content does not
      * preventDefault.
      */
-    ContentReceivedTouch(ScrollableLayerGuid aGuid, bool aPreventDefault);
+    ContentReceivedTouch(ScrollableLayerGuid aGuid, uint64_t aInputBlockId, bool aPreventDefault);
 
     /**
      * Updates the zoom constraints for a scrollable frame in this tab.
      * The zoom controller code lives on the parent side and so this allows it to
      * have up-to-date zoom constraints.
      */
     UpdateZoomConstraints(uint32_t aPresShellId, ViewID aViewId, bool aIsRoot,
                           ZoomConstraints aConstraints);
@@ -431,17 +431,17 @@ child:
     UpdateFrame(FrameMetrics frame);
 
     // The following methods correspond to functions on the GeckoContentController
     // interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
     // in that file for these functions.
     AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
     HandleDoubleTap(CSSPoint point, ScrollableLayerGuid aGuid);
     HandleSingleTap(CSSPoint point, ScrollableLayerGuid aGuid);
-    HandleLongTap(CSSPoint point, ScrollableLayerGuid aGuid);
+    HandleLongTap(CSSPoint point, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
     HandleLongTapUp(CSSPoint point, ScrollableLayerGuid aGuid);
     NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
 
 
     /**
      * Sending an activate message moves focus to the child.
      */
     Activate();
@@ -457,20 +457,20 @@ child:
                int32_t aButton,
                int32_t aClickCount,
                int32_t aModifiers,
                bool aIgnoreRootScrollFrame);
 
     RealMouseEvent(WidgetMouseEvent event);
     RealKeyEvent(WidgetKeyboardEvent event, MaybeNativeKeyBinding keyBinding);
     MouseWheelEvent(WidgetWheelEvent event);
-    RealTouchEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid);
+    RealTouchEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
     // We use a separate message for touchmove events only to apply
     // compression to them.
-    RealTouchMoveEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid) compress;
+    RealTouchMoveEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid, uint64_t aInputBlockId) compress;
 
     /**
      * @see nsIDOMWindowUtils sendKeyEvent.
      */
     KeyEvent(nsString aType,
              int32_t aKeyCode,
              int32_t aCharCode,
              int32_t aModifiers,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -821,16 +821,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mAppPackageFileDescriptorRecved(false)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mPendingTouchPreventedResponse(false)
+  , mPendingTouchPreventedBlockId(0)
   , mTouchEndCancelled(false)
   , mEndTouchIsClick(false)
   , mIgnoreKeyPressEvent(false)
   , mActiveElementManager(new ActiveElementManager())
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
 {
@@ -1506,17 +1507,17 @@ TabChild::HasValidInnerSize()
 }
 
 void
 TabChild::SendPendingTouchPreventedResponse(bool aPreventDefault,
                                             const ScrollableLayerGuid& aGuid)
 {
   if (mPendingTouchPreventedResponse) {
     MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
-    SendContentReceivedTouch(mPendingTouchPreventedGuid, aPreventDefault);
+    SendContentReceivedTouch(mPendingTouchPreventedGuid, mPendingTouchPreventedBlockId, aPreventDefault);
     mPendingTouchPreventedResponse = false;
   }
 }
 
 void
 TabChild::DestroyWindow()
 {
     nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
@@ -1956,17 +1957,17 @@ TabChild::FireSingleTapEvent(LayoutDevic
     Stringify(aPoint).c_str());
   int time = 0;
   DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, aPoint, mWidget);
   DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, aPoint, mWidget);
   DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, aPoint, mWidget);
 }
 
 bool
-TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
+TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
 {
   TABC_LOG("Handling long tap at %s with %p %p\n",
     Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
 
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
@@ -1986,17 +1987,17 @@ TabChild::RecvHandleLongTap(const CSSPoi
       APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();
     int time = 0;
     nsEventStatus status =
       DispatchSynthesizedMouseEvent(NS_MOUSE_MOZLONGTAP, time, currentPoint, mWidget);
     eventHandled = (status == nsEventStatus_eConsumeNoDefault);
     TABC_LOG("MOZLONGTAP event handled: %d\n", eventHandled);
   }
 
-  SendContentReceivedTouch(aGuid, eventHandled);
+  SendContentReceivedTouch(aGuid, aInputBlockId, eventHandled);
 
   return true;
 }
 
 bool
 TabChild::RecvHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   RecvHandleSingleTap(aPoint, aGuid);
@@ -2255,17 +2256,18 @@ TabChild::CancelTapTracking()
   if (mTapHoldTimer) {
     mTapHoldTimer->Cancel();
   }
   mTapHoldTimer = nullptr;
 }
 
 bool
 TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
-                             const ScrollableLayerGuid& aGuid)
+                             const ScrollableLayerGuid& aGuid,
+                             const uint64_t& aInputBlockId)
 {
   TABC_LOG("Receiving touch event of type %d\n", aEvent.message);
 
   WidgetTouchEvent localEvent(aEvent);
   localEvent.widget = mWidget;
   for (size_t i = 0; i < localEvent.touches.Length(); i++) {
     aEvent.touches[i]->mRefPoint = APZCCallbackHelper::ApplyCallbackTransform(aEvent.touches[i]->mRefPoint, aGuid, mWidget->GetDefaultScale());
   }
@@ -2284,24 +2286,25 @@ TabChild::RecvRealTouchEvent(const Widge
   bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
                           localEvent.mFlags.mMultipleActionsPrevented;
   switch (aEvent.message) {
   case NS_TOUCH_START: {
     mTouchEndCancelled = false;
     if (mPendingTouchPreventedResponse) {
       // We can enter here if we get two TOUCH_STARTs in a row and didn't
       // respond to the first one. Respond to it now.
-      SendContentReceivedTouch(mPendingTouchPreventedGuid, false);
+      SendContentReceivedTouch(mPendingTouchPreventedGuid, mPendingTouchPreventedBlockId, false);
       mPendingTouchPreventedResponse = false;
     }
     if (isTouchPrevented) {
-      SendContentReceivedTouch(aGuid, isTouchPrevented);
+      SendContentReceivedTouch(aGuid, aInputBlockId, isTouchPrevented);
     } else {
       mPendingTouchPreventedResponse = true;
       mPendingTouchPreventedGuid = aGuid;
+      mPendingTouchPreventedBlockId = aInputBlockId;
     }
     break;
   }
 
   case NS_TOUCH_END:
     if (isTouchPrevented) {
       mTouchEndCancelled = true;
       mEndTouchIsClick = false;
@@ -2319,19 +2322,20 @@ TabChild::RecvRealTouchEvent(const Widge
     NS_WARNING("Unknown touch event type");
   }
 
   return true;
 }
 
 bool
 TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
-                                 const ScrollableLayerGuid& aGuid)
+                                 const ScrollableLayerGuid& aGuid,
+                                 const uint64_t& aInputBlockId)
 {
-  return RecvRealTouchEvent(aEvent, aGuid);
+  return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId);
 }
 
 void
 TabChild::RequestNativeKeyBindings(AutoCacheNativeKeyCommands* aAutoCache,
                                    WidgetKeyboardEvent* aEvent)
 {
   MaybeNativeKeyBinding maybeBindings;
   if (!SendRequestNativeKeyBindings(*aEvent, &maybeBindings)) {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -319,17 +319,18 @@ public:
     virtual bool RecvUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
     virtual bool RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
                                              const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
     virtual bool RecvHandleDoubleTap(const CSSPoint& aPoint,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     virtual bool RecvHandleSingleTap(const CSSPoint& aPoint,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     virtual bool RecvHandleLongTap(const CSSPoint& aPoint,
-                                   const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
+                                   const mozilla::layers::ScrollableLayerGuid& aGuid,
+                                   const uint64_t& aInputBlockId) MOZ_OVERRIDE;
     virtual bool RecvHandleLongTapUp(const CSSPoint& aPoint,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId,
                                           const APZStateChange& aChange,
                                           const int& aArg) MOZ_OVERRIDE;
     virtual bool RecvActivate() MOZ_OVERRIDE;
     virtual bool RecvDeactivate() MOZ_OVERRIDE;
     virtual bool RecvMouseEvent(const nsString& aType,
@@ -339,19 +340,21 @@ public:
                                 const int32_t&  aClickCount,
                                 const int32_t&  aModifiers,
                                 const bool&     aIgnoreRootScrollFrame) MOZ_OVERRIDE;
     virtual bool RecvRealMouseEvent(const mozilla::WidgetMouseEvent& event) MOZ_OVERRIDE;
     virtual bool RecvRealKeyEvent(const mozilla::WidgetKeyboardEvent& event,
                                   const MaybeNativeKeyBinding& aBindings) MOZ_OVERRIDE;
     virtual bool RecvMouseWheelEvent(const mozilla::WidgetWheelEvent& event) MOZ_OVERRIDE;
     virtual bool RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
-                                    const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
+                                    const ScrollableLayerGuid& aGuid,
+                                    const uint64_t& aInputBlockId) MOZ_OVERRIDE;
     virtual bool RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
-                                        const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
+                                        const ScrollableLayerGuid& aGuid,
+                                        const uint64_t& aInputBlockId) MOZ_OVERRIDE;
     virtual bool RecvKeyEvent(const nsString& aType,
                               const int32_t&  aKeyCode,
                               const int32_t&  aCharCode,
                               const int32_t&  aModifiers,
                               const bool&     aPreventDefault) MOZ_OVERRIDE;
     virtual bool RecvCompositionEvent(const mozilla::WidgetCompositionEvent& event) MOZ_OVERRIDE;
     virtual bool RecvSelectionEvent(const mozilla::WidgetSelectionEvent& event) MOZ_OVERRIDE;
     virtual bool RecvActivateFrameEvent(const nsString& aType, const bool& capture) MOZ_OVERRIDE;
@@ -592,16 +595,17 @@ private:
     nscolor mLastBackgroundColor;
     bool mDidFakeShow;
     bool mNotified;
     bool mTriedBrowserInit;
     ScreenOrientation mOrientation;
     bool mUpdateHitRegion;
     bool mPendingTouchPreventedResponse;
     ScrollableLayerGuid mPendingTouchPreventedGuid;
+    uint64_t mPendingTouchPreventedBlockId;
     void FireSingleTapEvent(LayoutDevicePoint aPoint);
 
     bool mTouchEndCancelled;
     bool mEndTouchIsClick;
 
     bool mIgnoreKeyPressEvent;
     nsRefPtr<ActiveElementManager> mActiveElementManager;
     bool mHasValidInnerSize;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -639,20 +639,21 @@ void TabParent::HandleSingleTap(const CS
   // TODO Send the modifier data to TabChild for use in mouse events.
   if (!mIsDestroyed) {
     unused << SendHandleSingleTap(aPoint, aGuid);
   }
 }
 
 void TabParent::HandleLongTap(const CSSPoint& aPoint,
                               int32_t aModifiers,
-                              const ScrollableLayerGuid &aGuid)
+                              const ScrollableLayerGuid &aGuid,
+                              uint64_t aInputBlockId)
 {
   if (!mIsDestroyed) {
-    unused << SendHandleLongTap(aPoint, aGuid);
+    unused << SendHandleLongTap(aPoint, aGuid, aInputBlockId);
   }
 }
 
 void TabParent::HandleLongTapUp(const CSSPoint& aPoint,
                                 int32_t aModifiers,
                                 const ScrollableLayerGuid &aGuid)
 {
   if (!mIsDestroyed) {
@@ -872,17 +873,17 @@ TabParent::MapEventCoordinatesForChildPr
   }
 }
 
 bool TabParent::SendRealMouseEvent(WidgetMouseEvent& event)
 {
   if (mIsDestroyed) {
     return false;
   }
-  nsEventStatus status = MaybeForwardEventToRenderFrame(event, nullptr);
+  nsEventStatus status = MaybeForwardEventToRenderFrame(event, nullptr, nullptr);
   if (status == nsEventStatus_eConsumeNoDefault ||
       !MapEventCoordinatesForChildProcess(&event)) {
     return false;
   }
   return PBrowserParent::SendRealMouseEvent(event);
 }
 
 CSSPoint TabParent::AdjustTapToChildWidget(const CSSPoint& aPoint)
@@ -908,23 +909,23 @@ bool TabParent::SendHandleSingleTap(cons
 {
   if (mIsDestroyed) {
     return false;
   }
 
   return PBrowserParent::SendHandleSingleTap(AdjustTapToChildWidget(aPoint), aGuid);
 }
 
-bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
+bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
 {
   if (mIsDestroyed) {
     return false;
   }
 
-  return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aGuid);
+  return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aGuid, aInputBlockId);
 }
 
 bool TabParent::SendHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (mIsDestroyed) {
     return false;
   }
 
@@ -940,17 +941,17 @@ bool TabParent::SendHandleDoubleTap(cons
   return PBrowserParent::SendHandleDoubleTap(AdjustTapToChildWidget(aPoint), aGuid);
 }
 
 bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event)
 {
   if (mIsDestroyed) {
     return false;
   }
-  nsEventStatus status = MaybeForwardEventToRenderFrame(event, nullptr);
+  nsEventStatus status = MaybeForwardEventToRenderFrame(event, nullptr, nullptr);
   if (status == nsEventStatus_eConsumeNoDefault ||
       !MapEventCoordinatesForChildProcess(&event)) {
     return false;
   }
   return PBrowserParent::SendMouseWheelEvent(event);
 }
 
 static void
@@ -994,17 +995,17 @@ TabParent::RecvRequestNativeKeyBindings(
   return true;
 }
 
 bool TabParent::SendRealKeyEvent(WidgetKeyboardEvent& event)
 {
   if (mIsDestroyed) {
     return false;
   }
-  MaybeForwardEventToRenderFrame(event, nullptr);
+  MaybeForwardEventToRenderFrame(event, nullptr, nullptr);
   if (!MapEventCoordinatesForChildProcess(&event)) {
     return false;
   }
 
 
   MaybeNativeKeyBinding bindings;
   bindings = void_t();
   if (event.message == NS_KEY_PRESS) {
@@ -1062,27 +1063,28 @@ bool TabParent::SendRealTouchEvent(Widge
     for (int i = event.touches.Length() - 1; i >= 0; i--) {
       if (!event.touches[i]->mChanged) {
         event.touches.RemoveElementAt(i);
       }
     }
   }
 
   ScrollableLayerGuid guid;
-  nsEventStatus status = MaybeForwardEventToRenderFrame(event, &guid);
+  uint64_t blockId;
+  nsEventStatus status = MaybeForwardEventToRenderFrame(event, &guid, &blockId);
 
   if (status == nsEventStatus_eConsumeNoDefault || mIsDestroyed) {
     return false;
   }
 
   MapEventCoordinatesForChildProcess(mChildProcessOffsetAtTouchStart, &event);
 
   return (event.message == NS_TOUCH_MOVE) ?
-    PBrowserParent::SendRealTouchMoveEvent(event, guid) :
-    PBrowserParent::SendRealTouchEvent(event, guid);
+    PBrowserParent::SendRealTouchMoveEvent(event, guid, blockId) :
+    PBrowserParent::SendRealTouchEvent(event, guid, blockId);
 }
 
 /*static*/ TabParent*
 TabParent::GetEventCapturer()
 {
   return sEventCapturer;
 }
 
@@ -2005,20 +2007,21 @@ TabParent::UseAsyncPanZoom()
 {
   bool usingOffMainThreadCompositing = !!CompositorParent::CompositorLoop();
   return (usingOffMainThreadCompositing && gfxPrefs::AsyncPanZoomEnabled() &&
           GetScrollingBehavior() == ASYNC_PAN_ZOOM);
 }
 
 nsEventStatus
 TabParent::MaybeForwardEventToRenderFrame(WidgetInputEvent& aEvent,
-                                          ScrollableLayerGuid* aOutTargetGuid)
+                                          ScrollableLayerGuid* aOutTargetGuid,
+                                          uint64_t* aOutInputBlockId)
 {
   if (RenderFrameParent* rfp = GetRenderFrame()) {
-    return rfp->NotifyInputEvent(aEvent, aOutTargetGuid);
+    return rfp->NotifyInputEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
   }
   return nsEventStatus_eIgnore;
 }
 
 bool
 TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
                                       const nsString& aURL,
                                       const nsString& aName,
@@ -2062,20 +2065,21 @@ TabParent::RecvUpdateZoomConstraints(con
   if (RenderFrameParent* rfp = GetRenderFrame()) {
     rfp->UpdateZoomConstraints(aPresShellId, aViewId, aIsRoot, aConstraints);
   }
   return true;
 }
 
 bool
 TabParent::RecvContentReceivedTouch(const ScrollableLayerGuid& aGuid,
+                                    const uint64_t& aInputBlockId,
                                     const bool& aPreventDefault)
 {
   if (RenderFrameParent* rfp = GetRenderFrame()) {
-    rfp->ContentReceivedTouch(aGuid, aPreventDefault);
+    rfp->ContentReceivedTouch(aGuid, aInputBlockId, aPreventDefault);
   }
   return true;
 }
 
 already_AddRefed<nsILoadContext>
 TabParent::GetLoadContext()
 {
   nsCOMPtr<nsILoadContext> loadContext;
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -205,16 +205,17 @@ public:
     virtual bool RecvZoomToRect(const uint32_t& aPresShellId,
                                 const ViewID& aViewId,
                                 const CSSRect& aRect) MOZ_OVERRIDE;
     virtual bool RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
                                            const ViewID& aViewId,
                                            const bool& aIsRoot,
                                            const ZoomConstraints& aConstraints) MOZ_OVERRIDE;
     virtual bool RecvContentReceivedTouch(const ScrollableLayerGuid& aGuid,
+                                          const uint64_t& aInputBlockId,
                                           const bool& aPreventDefault) MOZ_OVERRIDE;
 
     virtual PColorPickerParent*
     AllocPColorPickerParent(const nsString& aTitle, const nsString& aInitialColor) MOZ_OVERRIDE;
     virtual bool DeallocPColorPickerParent(PColorPickerParent* aColorPicker) MOZ_OVERRIDE;
 
     void LoadURL(nsIURI* aURI);
     // XXX/cjones: it's not clear what we gain by hiding these
@@ -228,17 +229,18 @@ public:
     void HandleDoubleTap(const CSSPoint& aPoint,
                          int32_t aModifiers,
                          const ScrollableLayerGuid& aGuid);
     void HandleSingleTap(const CSSPoint& aPoint,
                          int32_t aModifiers,
                          const ScrollableLayerGuid& aGuid);
     void HandleLongTap(const CSSPoint& aPoint,
                        int32_t aModifiers,
-                       const ScrollableLayerGuid& aGuid);
+                       const ScrollableLayerGuid& aGuid,
+                       uint64_t aInputBlockId);
     void HandleLongTapUp(const CSSPoint& aPoint,
                          int32_t aModifiers,
                          const ScrollableLayerGuid& aGuid);
     void NotifyAPZStateChange(ViewID aViewId,
                               APZStateChange aChange,
                               int aArg);
     void Activate();
     void Deactivate();
@@ -256,17 +258,17 @@ public:
     void SendKeyEvent(const nsAString& aType, int32_t aKeyCode,
                       int32_t aCharCode, int32_t aModifiers,
                       bool aPreventDefault);
     bool SendRealMouseEvent(mozilla::WidgetMouseEvent& event);
     bool SendMouseWheelEvent(mozilla::WidgetWheelEvent& event);
     bool SendRealKeyEvent(mozilla::WidgetKeyboardEvent& event);
     bool SendRealTouchEvent(WidgetTouchEvent& event);
     bool SendHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
-    bool SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
+    bool SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId);
     bool SendHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
     bool SendHandleDoubleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
 
     virtual PDocumentRendererParent*
     AllocPDocumentRendererParent(const nsRect& documentRect,
                                  const gfx::Matrix& transform,
                                  const nsString& bgcolor,
                                  const uint32_t& renderFlags,
@@ -418,18 +420,22 @@ private:
     bool UseAsyncPanZoom();
     // If we have a render frame currently, notify it that we're about
     // to dispatch |aEvent| to our child.  If there's a relevant
     // transform in place, |aEvent| will be transformed in-place so that
     // it is ready to be dispatched to content.
     // |aOutTargetGuid| will contain the identifier
     // of the APZC instance that handled the event. aOutTargetGuid may be
     // null.
+    // |aOutInputBlockId| will contain the identifier of the input block
+    // that this event was added to, if there was one. aOutInputBlockId may
+    // be null.
     nsEventStatus MaybeForwardEventToRenderFrame(WidgetInputEvent& aEvent,
-                                                 ScrollableLayerGuid* aOutTargetGuid);
+                                                 ScrollableLayerGuid* aOutTargetGuid,
+                                                 uint64_t* aOutInputBlockId);
     // The offset for the child process which is sampled at touch start. This
     // means that the touch events are relative to where the frame was at the
     // start of the touch. We need to look for a better solution to this
     // problem see bug 872911.
     LayoutDeviceIntPoint mChildProcessOffsetAtTouchStart;
     // When true, we've initiated normal shutdown and notified our
     // managing PContent.
     bool mMarkedDestroying;
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -57,17 +57,18 @@ public:
                                const ScrollableLayerGuid& aGuid) = 0;
 
   /**
    * Requests handling a long tap. |aPoint| is in CSS pixels, relative to the
    * current scroll offset.
    */
   virtual void HandleLongTap(const CSSPoint& aPoint,
                              int32_t aModifiers,
-                             const ScrollableLayerGuid& aGuid) = 0;
+                             const ScrollableLayerGuid& aGuid,
+                             uint64_t aInputBlockId) = 0;
 
   /**
    * Requests handling of releasing a long tap. |aPoint| is in CSS pixels,
    * relative to the current scroll offset. HandleLongTapUp will always be
    * preceeded by HandleLongTap. However not all calls to HandleLongTap will
    * be followed by a HandleLongTapUp (for example, if the user drags
    * around between the long-tap and lifting their finger, or if content
    * notifies the APZ that the long-tap event was prevent-defaulted).
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -2,16 +2,17 @@
 /* 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 "APZCTreeManager.h"
 #include "AsyncPanZoomController.h"
 #include "Compositor.h"                 // for Compositor
 #include "CompositorParent.h"           // for CompositorParent, etc
+#include "InputBlockState.h"            // for InputBlockState
 #include "InputData.h"                  // for InputData, etc
 #include "Layers.h"                     // for Layer, etc
 #include "mozilla/dom/Touch.h"          // for Touch
 #include "mozilla/gfx/Point.h"          // for Point
 #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
 #include "mozilla/layers/LayerMetricsWrapper.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/mozalloc.h"           // for operator new
@@ -105,21 +106,22 @@ APZCTreeManager::GetAllowedTouchBehavior
     aOutValues.AppendElement(apzc
       ? apzc->GetAllowedTouchBehavior(spt)
       : AllowedTouchBehavior::UNKNOWN);
   }
 }
 
 void
 APZCTreeManager::SetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid,
+                                         uint64_t aInputBlockId,
                                          const nsTArray<TouchBehaviorFlags> &aValues)
 {
   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   if (apzc) {
-    apzc->SetAllowedTouchBehavior(aValues);
+    apzc->SetAllowedTouchBehavior(aInputBlockId, aValues);
   }
 }
 
 /* Flatten the tree of APZC instances into the given nsTArray */
 static void
 Collect(AsyncPanZoomController* aApzc, nsTArray< nsRefPtr<AsyncPanZoomController> >* aCollection)
 {
   if (aApzc) {
@@ -512,38 +514,44 @@ TransformScreenToGecko(T* aPoint, AsyncP
 {
   Matrix4x4 transformToApzc = aApzcTm->GetScreenToApzcTransform(aApzc);
   Matrix4x4 transformToGecko = aApzcTm->GetApzcToGeckoTransform(aApzc);
   ApplyTransform(aPoint, transformToApzc * transformToGecko);
 }
 
 nsEventStatus
 APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
-                                   ScrollableLayerGuid* aOutTargetGuid)
+                                   ScrollableLayerGuid* aOutTargetGuid,
+                                   uint64_t* aOutInputBlockId)
 {
+  // Initialize aOutInputBlockId to a sane value, and then later we overwrite
+  // it if the input event goes into a block.
+  if (aOutInputBlockId) {
+    *aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
+  }
   nsEventStatus result = nsEventStatus_eIgnore;
   Matrix4x4 transformToApzc;
   bool inOverscrolledApzc = false;
   switch (aEvent.mInputType) {
     case MULTITOUCH_INPUT: {
       MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
-      result = ProcessTouchInput(touchInput, aOutTargetGuid);
+      result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
       break;
     } case PANGESTURE_INPUT: {
       PanGestureInput& panInput = aEvent.AsPanGestureInput();
       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(panInput.mPanStartPoint,
                                                             &inOverscrolledApzc);
       if (apzc) {
         // When passing the event to the APZC, we need to apply a different
         // transform than the one in TransformScreenToGecko, so we need to
         // make a copy of the event.
         PanGestureInput inputForApzc(panInput);
         transformToApzc = GetScreenToApzcTransform(apzc);
         ApplyTransform(&(inputForApzc.mPanStartPoint), transformToApzc);
-        result = apzc->ReceiveInputEvent(inputForApzc);
+        result = apzc->ReceiveInputEvent(inputForApzc, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
         TransformScreenToGecko(&(panInput.mPanStartPoint), apzc, this);
       }
       break;
     } case PINCHGESTURE_INPUT: {
       PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
@@ -551,17 +559,17 @@ APZCTreeManager::ReceiveInputEvent(Input
                                                             &inOverscrolledApzc);
       if (apzc) {
         // When passing the event to the APZC, we need to apply a different
         // transform than the one in TransformScreenToGecko, so we need to
         // make a copy of the event.
         PinchGestureInput inputForApzc(pinchInput);
         transformToApzc = GetScreenToApzcTransform(apzc);
         ApplyTransform(&(inputForApzc.mFocusPoint), transformToApzc);
-        result = apzc->ReceiveInputEvent(inputForApzc);
+        result = apzc->ReceiveInputEvent(inputForApzc, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
         TransformScreenToGecko(&(pinchInput.mFocusPoint), apzc, this);
       }
       break;
     } case TAPGESTURE_INPUT: {
       TapGestureInput& tapInput = aEvent.AsTapGestureInput();
@@ -569,17 +577,17 @@ APZCTreeManager::ReceiveInputEvent(Input
                                                             &inOverscrolledApzc);
       if (apzc) {
         // When passing the event to the APZC, we need to apply a different
         // transform than the one in TransformScreenToGecko, so we need to
         // make a copy of the event.
         TapGestureInput inputForApzc(tapInput);
         transformToApzc = GetScreenToApzcTransform(apzc);
         ApplyTransform(&(inputForApzc.mPoint), transformToApzc);
-        result = apzc->ReceiveInputEvent(inputForApzc);
+        result = apzc->ReceiveInputEvent(inputForApzc, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
         TransformScreenToGecko(&(tapInput.mPoint), apzc, this);
       }
       break;
     }
   }
@@ -623,17 +631,18 @@ APZCTreeManager::GetTouchInputBlockAPZC(
     APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n", apzc.get());
   }
 
   return apzc.forget();
 }
 
 nsEventStatus
 APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
-                                   ScrollableLayerGuid* aOutTargetGuid)
+                                   ScrollableLayerGuid* aOutTargetGuid,
+                                   uint64_t* aOutInputBlockId)
 {
   if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
     // If we are in an overscrolled state and a second finger goes down,
     // ignore that second touch point completely. The touch-start for it is
     // dropped completely; subsequent touch events until the touch-end for it
     // will have this touch point filtered out.
     if (mApzcForInputBlock && BuildOverscrollHandoffChain(mApzcForInputBlock)->HasOverscrolledApzc()) {
       if (mRetainedTouchIdentifier == -1) {
@@ -648,17 +657,17 @@ APZCTreeManager::ProcessTouchInput(Multi
     mInOverscrolledApzc = false;
     nsRefPtr<AsyncPanZoomController> apzc = GetTouchInputBlockAPZC(aInput, &mInOverscrolledApzc);
     if (apzc != mApzcForInputBlock) {
       // If we're moving to a different APZC as our input target, then send a cancel event
       // to the old one so that it clears its internal state. Otherwise it could get left
       // in the middle of a panning touch block (for example) and not clean up properly.
       if (mApzcForInputBlock) {
         MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
-        mApzcForInputBlock->ReceiveInputEvent(cancel);
+        mApzcForInputBlock->ReceiveInputEvent(cancel, nullptr);
       }
       mApzcForInputBlock = apzc;
     }
 
     if (mApzcForInputBlock) {
       // Cache apz transform so it can be used for future events in this block.
       mCachedTransformToApzcForInputBlock = GetScreenToApzcTransform(mApzcForInputBlock);
     } else {
@@ -697,17 +706,17 @@ APZCTreeManager::ProcessTouchInput(Multi
     // For computing the input for the APZC, used the cached transform.
     // This ensures that the sequence of touch points an APZC sees in an
     // input block are all in the same coordinate space.
     Matrix4x4 transformToApzc = mCachedTransformToApzcForInputBlock;
     MultiTouchInput inputForApzc(aInput);
     for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
       ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
     }
-    result = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
+    result = mApzcForInputBlock->ReceiveInputEvent(inputForApzc, aOutInputBlockId);
 
     // For computing the event to pass back to Gecko, use the up-to-date transforms.
     // This ensures that transformToApzc and transformToGecko are in sync
     // (note that transformToGecko isn't cached).
     transformToApzc = GetScreenToApzcTransform(mApzcForInputBlock);
     Matrix4x4 transformToGecko = GetApzcToGeckoTransform(mApzcForInputBlock);
     Matrix4x4 outTransform = transformToApzc * transformToGecko;
     for (size_t i = 0; i < aInput.mTouches.Length(); i++) {
@@ -752,17 +761,18 @@ APZCTreeManager::TransformCoordinateToGe
     Matrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
     Matrix4x4 outTransform = transformToApzc * transformToGecko;
     *aOutTransformedPoint = TransformTo<LayoutDevicePixel>(outTransform, aPoint);
   }
 }
 
 nsEventStatus
 APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent,
-                              ScrollableLayerGuid* aOutTargetGuid)
+                              ScrollableLayerGuid* aOutTargetGuid,
+                              uint64_t* aOutInputBlockId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsEventStatus result = nsEventStatus_eIgnore;
 
   // Transform the refPoint.
   // If the event hits an overscrolled APZC, instruct the caller to ignore it.
   bool inOverscrolledApzc = false;
   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(ScreenPoint(aEvent.refPoint.x, aEvent.refPoint.y),
@@ -777,63 +787,71 @@ APZCTreeManager::ProcessEvent(WidgetInpu
   if (inOverscrolledApzc) {
     result = nsEventStatus_eConsumeNoDefault;
   }
   return result;
 }
 
 nsEventStatus
 APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
-                                   ScrollableLayerGuid* aOutTargetGuid)
+                                   ScrollableLayerGuid* aOutTargetGuid,
+                                   uint64_t* aOutInputBlockId)
 {
   // This function will be removed as part of bug 930939.
   // In general it is preferable to use the version of ReceiveInputEvent
   // that takes an InputData, as that is usable from off-main-thread.
 
   MOZ_ASSERT(NS_IsMainThread());
 
+  // Initialize aOutInputBlockId to a sane value, and then later we overwrite
+  // it if the input event goes into a block.
+  if (aOutInputBlockId) {
+    *aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
+  }
+
   switch (aEvent.mClass) {
     case eTouchEventClass: {
       WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
       MultiTouchInput touchInput(touchEvent);
-      nsEventStatus result = ProcessTouchInput(touchInput, aOutTargetGuid);
+      nsEventStatus result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
       // touchInput was modified in-place to possibly remove some
       // touch points (if we are overscrolled), and the coordinates were
       // modified using the APZ untransform. We need to copy these changes
       // back into the WidgetInputEvent.
       touchEvent.touches.Clear();
       touchEvent.touches.SetCapacity(touchInput.mTouches.Length());
       for (size_t i = 0; i < touchInput.mTouches.Length(); i++) {
         *touchEvent.touches.AppendElement() = touchInput.mTouches[i].ToNewDOMTouch();
       }
       return result;
     }
     default: {
-      return ProcessEvent(aEvent, aOutTargetGuid);
+      return ProcessEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
     }
   }
 }
 
 void
 APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
                             const CSSRect& aRect)
 {
   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   if (apzc) {
     apzc->ZoomToRect(aRect);
   }
 }
 
 void
 APZCTreeManager::ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
+                                      uint64_t aInputBlockId,
                                       bool aPreventDefault)
 {
   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   if (apzc) {
-    apzc->ContentReceivedTouch(aPreventDefault);
+    apzc->ContentReceivedTouch(aInputBlockId, aPreventDefault);
   }
 }
 
 void
 APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
                                        const ZoomConstraints& aConstraints)
 {
   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -154,39 +154,40 @@ public:
    *   in some cases CONSUMED is returned even if the event was NOT used. This
    *   is because we cannot always know at the time of event delivery whether
    *   the event will be used or not. So we err on the side of sending
    *   CONSUMED when we are uncertain.
    *
    * @param aEvent input event object; is modified in-place
    * @param aOutTargetGuid returns the guid of the apzc this event was
    * delivered to. May be null.
+   * @param aOutInputBlockId returns the id of the input block that this event
+   * was added to, if that was the case. May be null.
    */
   nsEventStatus ReceiveInputEvent(InputData& aEvent,
-                                  ScrollableLayerGuid* aOutTargetGuid);
+                                  ScrollableLayerGuid* aOutTargetGuid,
+                                  uint64_t* aOutInputBlockId);
 
   /**
    * WidgetInputEvent handler. Transforms |aEvent| (which is assumed to be an
    * already-existing instance of an WidgetInputEvent which may be an
    * WidgetTouchEvent) to have its coordinates in DOM space. This is so that the
    * event can be passed through the DOM and content can handle them.
    *
    * NOTE: Be careful of invoking the WidgetInputEvent variant. This can only be
    * called on the main thread. See widget/InputData.h for more information on
    * why we have InputData and WidgetInputEvent separated.
    * NOTE: On unix, mouse events are treated as touch and are forwarded
    * to the appropriate apz as such.
    *
-   * @param aEvent input event object; is modified in-place
-   * @param aOutTargetGuid returns the guid of the apzc this event was
-   * delivered to. May be null.
-   * @return See documentation for other ReceiveInputEvent above.
+   * See documentation for other ReceiveInputEvent above.
    */
   nsEventStatus ReceiveInputEvent(WidgetInputEvent& aEvent,
-                                  ScrollableLayerGuid* aOutTargetGuid);
+                                  ScrollableLayerGuid* aOutTargetGuid,
+                                  uint64_t* aOutInputBlockId);
 
   /**
    * A helper for transforming coordinates to gecko coordinate space.
    *
    * @param aPoint point to transform
    * @param aOutTransformedPoint resulting transformed point
    */
   void TransformCoordinateToGecko(const ScreenIntPoint& aPoint,
@@ -202,16 +203,17 @@ public:
 
   /**
    * If we have touch listeners, this should always be called when we know
    * definitively whether or not content has preventDefaulted any touch events
    * that have come in. If |aPreventDefault| is true, any touch events in the
    * queue will be discarded.
    */
   void ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
+                            uint64_t aInputBlockId,
                             bool aPreventDefault);
 
   /**
    * Updates any zoom constraints contained in the <meta name="viewport"> tag.
    */
   void UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
                              const ZoomConstraints& aConstraints);
 
@@ -261,22 +263,25 @@ public:
    * Returns values of allowed touch-behavior for the touches of aEvent via out parameter.
    * Internally performs asks appropriate AsyncPanZoomController to perform
    * hit testing on its own.
    */
   void GetAllowedTouchBehavior(WidgetInputEvent* aEvent,
                                nsTArray<TouchBehaviorFlags>& aOutValues);
 
   /**
-   * Sets allowed touch behavior values for current touch-session for specific apzc (determined by guid).
-   * Should be invoked by the widget. Each value of the aValues arrays corresponds to the different
-   * touch point that is currently active.
-   * Must be called after receiving the TOUCH_START event that starts the touch-session.
+   * Sets allowed touch behavior values for current touch-session for specific
+   * apzc and input block (determined by aGuid and aInputBlock).
+   * Should be invoked by the widget. Each value of the aValues arrays
+   * corresponds to the different touch point that is currently active.
+   * Must be called after receiving the TOUCH_START event that starts the
+   * touch-session.
    */
   void SetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid,
+                               uint64_t aInputBlockId,
                                const nsTArray<TouchBehaviorFlags>& aValues);
 
   /**
    * This is a callback for AsyncPanZoomController to call when it wants to
    * scroll in response to a touch-move event, or when it needs to hand off
    * overscroll to the next APZC. Note that because of scroll grabbing, the
    * first APZC to scroll may not be the one that is receiving the touch events.
    *
@@ -378,19 +383,21 @@ private:
   AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc,
                                          const gfx::Point& aHitTestPoint,
                                          bool* aOutInOverscrolledApzc);
   already_AddRefed<AsyncPanZoomController> CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2);
   already_AddRefed<AsyncPanZoomController> RootAPZCForLayersId(AsyncPanZoomController* aApzc);
   already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
                                                                   bool* aOutInOverscrolledApzc);
   nsEventStatus ProcessTouchInput(MultiTouchInput& aInput,
-                                  ScrollableLayerGuid* aOutTargetGuid);
+                                  ScrollableLayerGuid* aOutTargetGuid,
+                                  uint64_t* aOutInputBlockId);
   nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent,
-                             ScrollableLayerGuid* aOutTargetGuid);
+                             ScrollableLayerGuid* aOutTargetGuid,
+                             uint64_t* aOutInputBlockId);
   void UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
                                         const ZoomConstraints& aConstraints);
   void FlushRepaintsRecursively(AsyncPanZoomController* aApzc);
 
   AsyncPanZoomController* PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
                                               const FrameMetrics& aMetrics,
                                               uint64_t aLayersId,
                                               const gfx::Matrix4x4& aAncestorTransform,
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1008,18 +1008,18 @@ AsyncPanZoomController::ArePointerEvents
   bool consumable = (aTouchPoints == 1 ? pannable : zoomable);
   if (!consumable) {
     return false;
   }
 
   return true;
 }
 
-nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
-  return GetInputQueue()->ReceiveInputEvent(this, aEvent);
+nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent, uint64_t* aOutInputBlockId) {
+  return GetInputQueue()->ReceiveInputEvent(this, aEvent, aOutInputBlockId);
 }
 
 nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
   AssertOnControllerThread();
 
   nsEventStatus rv = nsEventStatus_eIgnore;
 
   switch (aEvent.mInputType) {
@@ -1574,18 +1574,18 @@ nsEventStatus AsyncPanZoomController::On
 
 nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a long-press in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
     CSSPoint geckoScreenPoint;
     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
-      GetInputQueue()->InjectNewTouchBlock(this);
-      controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid());
+      uint64_t blockId = GetInputQueue()->InjectNewTouchBlock(this);
+      controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid(), blockId);
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
@@ -2850,23 +2850,23 @@ void AsyncPanZoomController::ZoomToRect(
 
     // Schedule a repaint now, so the new displayport will be painted before the
     // animation finishes.
     RequestContentRepaint(endZoomToMetrics);
   }
 }
 
 void
-AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
-  GetInputQueue()->ContentReceivedTouch(aPreventDefault);
+AsyncPanZoomController::ContentReceivedTouch(uint64_t aInputBlockId, bool aPreventDefault) {
+  GetInputQueue()->ContentReceivedTouch(aInputBlockId, aPreventDefault);
 }
 
 void
-AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
-  GetInputQueue()->SetAllowedTouchBehavior(aBehaviors);
+AsyncPanZoomController::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
+  GetInputQueue()->SetAllowedTouchBehavior(aInputBlockId, aBehaviors);
 }
 
 bool
 AsyncPanZoomController::NeedToWaitForContent() const
 {
   return (mFrameMetrics.GetMayHaveTouchListeners() || mFrameMetrics.GetMayHaveTouchCaret());
 }
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -117,17 +117,17 @@ public:
   /**
    * General handler for incoming input events. Manipulates the frame metrics
    * based on what type of input it is. For example, a PinchGestureEvent will
    * cause scaling. This should only be called externally to this class.
    * HandleInputEvent() should be used internally.
    * See the documentation on APZCTreeManager::ReceiveInputEvent for info on
    * return values from this function.
    */
-  nsEventStatus ReceiveInputEvent(const InputData& aEvent);
+  nsEventStatus ReceiveInputEvent(const InputData& aEvent, uint64_t* aOutInputBlockId);
 
   /**
    * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
    * in. The actual animation is done on the compositor thread after being set
    * up.
    */
   void ZoomToRect(CSSRect aRect);
 
@@ -771,22 +771,22 @@ private:
    * The functions and members in this section are used to manage
    * blocks of touch events and the state needed to deal with content
    * listeners.
    */
 public:
   /**
    * See InputQueue::ContentReceivedTouch
    */
-  void ContentReceivedTouch(bool aPreventDefault);
+  void ContentReceivedTouch(uint64_t aInputBlockId, bool aPreventDefault);
 
   /**
    * See InputQueue::SetAllowedTouchBehavior
    */
-  void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
+  void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors);
 
   /**
    * Flush a repaint request if one is needed, without throttling it with the
    * paint throttler.
    */
   void FlushRepaintForNewInputBlock();
 
   /**
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -10,18 +10,21 @@
 #include "OverscrollHandoffState.h"
 
 #define TBS_LOG(...)
 // #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
+static uint64_t sBlockCounter = InputBlockState::NO_BLOCK_ID + 1;
+
 InputBlockState::InputBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc)
   : mTargetApzc(aTargetApzc)
+  , mBlockId(sBlockCounter++)
 {
   // We should never be constructed with a nullptr target.
   MOZ_ASSERT(mTargetApzc);
   mOverscrollHandoffChain = mTargetApzc->BuildOverscrollHandoffChain();
 }
 
 const nsRefPtr<AsyncPanZoomController>&
 InputBlockState::GetTargetApzc() const
@@ -30,16 +33,22 @@ InputBlockState::GetTargetApzc() const
 }
 
 const nsRefPtr<const OverscrollHandoffChain>&
 InputBlockState::GetOverscrollHandoffChain() const
 {
   return mOverscrollHandoffChain;
 }
 
+uint64_t
+InputBlockState::GetBlockId() const
+{
+  return mBlockId;
+}
+
 TouchBlockState::TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc)
   : InputBlockState(aTargetApzc)
   , mAllowedTouchBehaviorSet(false)
   , mPreventDefault(false)
   , mContentResponded(false)
   , mContentResponseTimerExpired(false)
   , mSingleTapDisallowed(false)
   , mSingleTapOccurred(false)
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -18,24 +18,27 @@ class OverscrollHandoffChain;
 
 /**
  * A base class that stores state common to various input blocks.
  * Currently, it just stores the overscroll handoff chain.
  */
 class InputBlockState
 {
 public:
+  static const uint64_t NO_BLOCK_ID = 0;
+
   explicit InputBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc);
 
   const nsRefPtr<AsyncPanZoomController>& GetTargetApzc() const;
   const nsRefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
-
+  uint64_t GetBlockId() const;
 private:
   nsRefPtr<AsyncPanZoomController> mTargetApzc;
   nsRefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
+  const uint64_t mBlockId;
 };
 
 /**
  * This class represents a single touch block. A touch block is
  * a set of touch events that can be cancelled by web content via
  * touch event listeners.
  *
  * Every touch-start event creates a new touch block. In this case, the
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -13,26 +13,25 @@
 
 #define INPQ_LOG(...)
 // #define INPQ_LOG(...) printf_stderr("INPQ: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
 InputQueue::InputQueue()
-  : mTouchBlockBalance(0)
 {
 }
 
 InputQueue::~InputQueue() {
   mTouchBlockQueue.Clear();
 }
 
 nsEventStatus
-InputQueue::ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget, const InputData& aEvent) {
+InputQueue::ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget, const InputData& aEvent, uint64_t* aOutInputBlockId) {
   AsyncPanZoomController::AssertOnControllerThread();
 
   if (aEvent.mInputType != MULTITOUCH_INPUT) {
     aTarget->HandleInputEvent(aEvent);
     // The return value for non-touch input isn't really used, so just return
     // ConsumeDoDefault for now. This can be changed later if needed.
     return nsEventStatus_eConsumeDoDefault;
   }
@@ -55,36 +54,38 @@ InputQueue::ReceiveInputEvent(const nsRe
         block->DisallowSingleTap();
       }
       block->GetOverscrollHandoffChain()->CancelAnimations();
     }
 
     if (aTarget->NeedToWaitForContent()) {
       // Content may intercept the touch events and prevent-default them. So we schedule
       // a timeout to give content time to do that.
-      ScheduleContentResponseTimeout(aTarget);
+      ScheduleContentResponseTimeout(aTarget, block->GetBlockId());
     } else {
       // Content won't prevent-default this, so we can just pretend like we scheduled
       // a timeout and it expired. Note that we will still receive a ContentReceivedTouch
       // callback for this block, and so we need to make sure we adjust the touch balance.
       INPQ_LOG("%p not waiting for content response on block %p\n", this, block);
-      mTouchBlockBalance++;
       block->TimeoutContentResponse();
     }
   } else if (mTouchBlockQueue.IsEmpty()) {
     NS_WARNING("Received a non-start touch event while no touch blocks active!");
   } else {
     // this touch is part of the most-recently created block
     block = mTouchBlockQueue.LastElement().get();
     INPQ_LOG("%p received new event in block %p\n", this, block);
   }
 
   if (!block) {
     return nsEventStatus_eIgnore;
   }
+  if (aOutInputBlockId) {
+    *aOutInputBlockId = block->GetBlockId();
+  }
 
   nsEventStatus result = aTarget->ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())
       ? nsEventStatus_eConsumeDoDefault
       : nsEventStatus_eIgnore;
 
   if (block == CurrentTouchBlock() && block->IsReadyForHandling()) {
     INPQ_LOG("%p's current touch block is ready with preventdefault %d\n",
         this, block->IsDefaultPrevented());
@@ -95,21 +96,24 @@ InputQueue::ReceiveInputEvent(const nsRe
     return result;
   }
 
   // Otherwise, add it to the queue for the touch block
   block->AddEvent(aEvent.AsMultiTouchInput());
   return result;
 }
 
-void
+uint64_t
 InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
 {
-  StartNewTouchBlock(aTarget, true);
-  ScheduleContentResponseTimeout(aTarget);
+  TouchBlockState* block = StartNewTouchBlock(aTarget, true);
+  INPQ_LOG("%p injecting new touch block with id %llu and target %p\n",
+    this, block->GetBlockId(), aTarget);
+  ScheduleContentResponseTimeout(aTarget, block->GetBlockId());
+  return block->GetBlockId();
 }
 
 TouchBlockState*
 InputQueue::StartNewTouchBlock(const nsRefPtr<AsyncPanZoomController>& aTarget, bool aCopyAllowedTouchBehaviorFromCurrent)
 {
   TouchBlockState* newBlock = new TouchBlockState(aTarget);
   if (gfxPrefs::TouchActionEnabled() && aCopyAllowedTouchBehaviorFromCurrent) {
     newBlock->CopyAllowedTouchBehaviorsFrom(*CurrentTouchBlock());
@@ -142,86 +146,73 @@ InputQueue::CurrentTouchBlock() const
 
 bool
 InputQueue::HasReadyTouchBlock() const
 {
   return !mTouchBlockQueue.IsEmpty() && mTouchBlockQueue[0]->IsReadyForHandling();
 }
 
 void
-InputQueue::ScheduleContentResponseTimeout(const nsRefPtr<AsyncPanZoomController>& aTarget) {
+InputQueue::ScheduleContentResponseTimeout(const nsRefPtr<AsyncPanZoomController>& aTarget, uint64_t aInputBlockId) {
   INPQ_LOG("%p scheduling content response timeout for target %p\n", this, aTarget.get());
   aTarget->PostDelayedTask(
-    NewRunnableMethod(this, &InputQueue::ContentResponseTimeout),
+    NewRunnableMethod(this, &InputQueue::ContentResponseTimeout, aInputBlockId),
     gfxPrefs::APZContentResponseTimeout());
 }
 
 void
-InputQueue::ContentResponseTimeout() {
+InputQueue::ContentResponseTimeout(const uint64_t& aInputBlockId) {
   AsyncPanZoomController::AssertOnControllerThread();
 
-  mTouchBlockBalance++;
-  INPQ_LOG("%p got a content response timeout; balance %d\n", this, mTouchBlockBalance);
-  if (mTouchBlockBalance > 0) {
-    // Find the first touch block in the queue that hasn't already received
-    // the content response timeout callback, and notify it.
-    bool found = false;
-    for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
-      if (mTouchBlockQueue[i]->TimeoutContentResponse()) {
-        found = true;
-        break;
-      }
+  INPQ_LOG("%p got a content response timeout; block=%llu\n", this, aInputBlockId);
+  bool success = false;
+  for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
+    if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
+      success = mTouchBlockQueue[i]->TimeoutContentResponse();
+      break;
     }
-    if (found) {
-      ProcessPendingInputBlocks();
-    } else {
-      NS_WARNING("INPQ received more ContentResponseTimeout calls than it has unprocessed touch blocks\n");
-    }
+  }
+  if (success) {
+    ProcessPendingInputBlocks();
   }
 }
 
 void
-InputQueue::ContentReceivedTouch(bool aPreventDefault) {
+InputQueue::ContentReceivedTouch(uint64_t aInputBlockId, bool aPreventDefault) {
   AsyncPanZoomController::AssertOnControllerThread();
 
-  mTouchBlockBalance--;
-  INPQ_LOG("%p got a content response; balance %d\n", this, mTouchBlockBalance);
-  if (mTouchBlockBalance < 0) {
-    // Find the first touch block in the queue that hasn't already received
-    // its response from content, and notify it.
-    bool found = false;
-    for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
-      if (mTouchBlockQueue[i]->SetContentResponse(aPreventDefault)) {
-        found = true;
-        break;
-      }
+  INPQ_LOG("%p got a content response; block=%llu\n", this, aInputBlockId);
+  bool success = false;
+  for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
+    if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
+      success = mTouchBlockQueue[i]->SetContentResponse(aPreventDefault);
+      break;
     }
-    if (found) {
-      ProcessPendingInputBlocks();
-    } else {
-      NS_WARNING("INPQ received more ContentReceivedTouch calls than it has unprocessed touch blocks\n");
-    }
+  }
+  if (success) {
+    ProcessPendingInputBlocks();
   }
 }
 
 void
-InputQueue::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
+InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
   AsyncPanZoomController::AssertOnControllerThread();
 
-  bool found = false;
+  INPQ_LOG("%p got allowed touch behaviours; block=%llu\n", this, aInputBlockId);
+  bool success = false;
   for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
-    if (mTouchBlockQueue[i]->SetAllowedTouchBehaviors(aBehaviors)) {
-      found = true;
+    if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
+      success = mTouchBlockQueue[i]->SetAllowedTouchBehaviors(aBehaviors);
       break;
     }
   }
-  if (found) {
+  if (success) {
     ProcessPendingInputBlocks();
   } else {
-    NS_WARNING("INPQ received more SetAllowedTouchBehavior calls than it has unprocessed touch blocks\n");
+    NS_WARNING("INPQ received useless SetAllowedTouchBehavior");
   }
 }
 
 void
 InputQueue::ProcessPendingInputBlocks() {
   AsyncPanZoomController::AssertOnControllerThread();
 
   while (true) {
--- a/gfx/layers/apz/src/InputQueue.h
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -34,114 +34,60 @@ public:
 
 public:
   InputQueue();
 
   /**
    * Notifies the InputQueue of a new incoming input event. The APZC that the
    * input event was targeted to should be provided in the |aTarget| parameter.
    * See the documentation on APZCTreeManager::ReceiveInputEvent for info on
-   * return values from this function.
+   * return values from this function, including |aOutInputBlockId|.
    */
-  nsEventStatus ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget, const InputData& aEvent);
+  nsEventStatus ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget, const InputData& aEvent, uint64_t* aOutInputBlockId);
   /**
    * This function should be invoked to notify the InputQueue when web content
-   * decides whether or not it wants to cancel a block of events. This
-   * automatically gets applied to the next block of events that has not yet
-   * been responded to. This function MUST be invoked exactly once for each
-   * touch block, after the touch-start event that creates the block is sent to
-   * ReceiveInputEvent.
+   * decides whether or not it wants to cancel a block of events. The block
+   * id to which this applies should be provided in |aInputBlockId|.
    */
-  void ContentReceivedTouch(bool aPreventDefault);
+  void ContentReceivedTouch(uint64_t aInputBlockId, bool aPreventDefault);
   /**
    * This function should be invoked to notify the InputQueue of the touch-
-   * action properties for the different touch points in an input block. This
-   * automatically gets applied to the next block of events that has not yet
-   * received a touch behaviour notification. This function MUST be invoked
-   * exactly once for each touch block, after the touch-start event that creates
-   * the block is sent to ReceiveInputEvent. If touch-action is not enabled on
-   * the platform, this function does nothing and need not be called.
+   * action properties for the different touch points in an input block. The
+   * input block this applies to should be specified by the |aInputBlockId|
+   * parameter. If touch-action is not enabled on the platform, this function
+   * does nothing and need not be called.
    */
-  void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
+  void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors);
   /**
    * Adds a new touch block at the end of the input queue that has the same
    * allowed touch behaviour flags as the the touch block currently being
    * processed. This should only be called when processing of a touch block
-   * triggers the creation of a new touch block.
+   * triggers the creation of a new touch block. Returns the input block id
+   * of the the newly-created block.
    */
-  void InjectNewTouchBlock(AsyncPanZoomController* aTarget);
+  uint64_t InjectNewTouchBlock(AsyncPanZoomController* aTarget);
   /**
    * Returns the touch block at the head of the queue.
    */
   TouchBlockState* CurrentTouchBlock() const;
   /**
    * Returns true iff the touch block at the head of the queue is ready for
    * handling.
    */
   bool HasReadyTouchBlock() const;
 
 private:
   ~InputQueue();
   TouchBlockState* StartNewTouchBlock(const nsRefPtr<AsyncPanZoomController>& aTarget, bool aCopyAllowedTouchBehaviorFromCurrent);
-  void ScheduleContentResponseTimeout(const nsRefPtr<AsyncPanZoomController>& aTarget);
-  void ContentResponseTimeout();
+  void ScheduleContentResponseTimeout(const nsRefPtr<AsyncPanZoomController>& aTarget, uint64_t aInputBlockId);
+  void ContentResponseTimeout(const uint64_t& aInputBlockId);
   void ProcessPendingInputBlocks();
 
 private:
   // The queue of touch blocks that have not yet been processed.
   // This member must only be accessed on the controller/UI thread.
   nsTArray<UniquePtr<TouchBlockState>> mTouchBlockQueue;
-
-  // This variable requires some explanation. Strap yourself in.
-  //
-  // For each block of events, we do two things: (1) send the events to gecko and expect
-  // exactly one call to ContentReceivedTouch in return, and (2) kick off a timeout
-  // that triggers in case we don't hear from web content in a timely fashion.
-  // Since events are constantly coming in, we need to be able to handle more than one
-  // block of input events sitting in the queue.
-  //
-  // There are ordering restrictions on events that we can take advantage of, and that
-  // we need to abide by. Blocks of events in the queue will always be in the order that
-  // the user generated them. Responses we get from content will be in the same order as
-  // as the blocks of events in the queue. The timeout callbacks that have been posted
-  // will also fire in the same order as the blocks of events in the queue.
-  // HOWEVER, we may get multiple responses from content interleaved with multiple
-  // timeout expirations, and that interleaving is not predictable.
-  //
-  // Therefore, we need to make sure that for each block of events, we process the queued
-  // events exactly once, either when we get the response from content, or when the
-  // timeout expires (whichever happens first). There is no way to associate the timeout
-  // or response from content with a particular block of events other than via ordering.
-  //
-  // So, what we do to accomplish this is to track a "touch block balance", which is the
-  // number of timeout expirations that have fired, minus the number of content responses
-  // that have been received. (Think "balance" as in teeter-totter balance). This
-  // value is:
-  // - zero when we are in a state where the next content response we expect to receive
-  //   and the next timeout expiration we expect to fire both correspond to the next
-  //   unprocessed block of events in the queue.
-  // - negative when we are in a state where we have received more content responses than
-  //   timeout expirations. This means that the next content repsonse we receive will
-  //   correspond to the first unprocessed block, but the next n timeout expirations need
-  //   to be ignored as they are for blocks we have already processed. (n is the absolute
-  //   value of the balance.)
-  // - positive when we are in a state where we have received more timeout expirations
-  //   than content responses. This means that the next timeout expiration that we will
-  //   receive will correspond to the first unprocessed block, but the next n content
-  //   responses need to be ignored as they are for blocks we have already processed.
-  //   (n is the absolute value of the balance.)
-  //
-  // Note that each touch block internally carries flags that indicate whether or not it
-  // has received a content response and/or timeout expiration. However, we cannot rely
-  // on that alone to deliver these notifications to the right input block, because
-  // once an input block has been processed, it can potentially be removed from the queue.
-  // Therefore the information in that block is lost. An alternative approach would
-  // be to keep around those blocks until they have received both the content response
-  // and timeout expiration, but that involves a higher level of memory usage.
-  //
-  // This member must only be accessed on the controller/UI thread.
-  int32_t mTouchBlockBalance;
 };
 
 }
 }
 
 #endif // mozilla_layers_InputQueue_h
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -57,17 +57,17 @@ private:
     prefValue)
 
 class MockContentController : public GeckoContentController {
 public:
   MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
   MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
   MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
   MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
-  MOCK_METHOD3(HandleLongTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
+  MOCK_METHOD4(HandleLongTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&, uint64_t));
   MOCK_METHOD3(HandleLongTapUp, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
   MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize));
   MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs));
   MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
 };
 
 class MockContentControllerDelayed : public MockContentController {
 public:
@@ -238,29 +238,29 @@ class APZCGestureDetectorTester : public
 public:
   APZCGestureDetectorTester()
     : APZCBasicTester(AsyncPanZoomController::USE_GESTURE_DETECTOR)
   {
   }
 };
 
 static nsEventStatus
-ApzcDown(AsyncPanZoomController* apzc, int aX, int aY, int aTime)
+ApzcDown(AsyncPanZoomController* apzc, int aX, int aY, int aTime, uint64_t* aOutInputBlockId = nullptr)
 {
   MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
-  return apzc->ReceiveInputEvent(mti);
+  return apzc->ReceiveInputEvent(mti, aOutInputBlockId);
 }
 
 static nsEventStatus
 ApzcUp(AsyncPanZoomController* apzc, int aX, int aY, int aTime)
 {
   MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
-  return apzc->ReceiveInputEvent(mti);
+  return apzc->ReceiveInputEvent(mti, nullptr);
 }
 
 static void
 ApzcTap(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime, int aTapLength,
         nsEventStatus (*aOutEventStatuses)[2] = nullptr)
 {
   nsEventStatus status = ApzcDown(aApzc, aX, aY, aTime);
   if (aOutEventStatuses) {
@@ -284,46 +284,54 @@ ApzcTapAndCheckStatus(AsyncPanZoomContro
 
 static void
 ApzcPan(AsyncPanZoomController* aApzc,
         int& aTime,
         int aTouchStartY,
         int aTouchEndY,
         bool aKeepFingerDown = false,
         nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
-        nsEventStatus (*aOutEventStatuses)[4] = nullptr)
+        nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+        uint64_t* aOutInputBlockId = nullptr)
 {
   const int TIME_BETWEEN_TOUCH_EVENT = 100;
   const int OVERCOME_TOUCH_TOLERANCE = 100;
 
+  // Even if the caller doesn't care about the block id, we need it to set the
+  // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
+  uint64_t blockId;
+  if (!aOutInputBlockId) {
+    aOutInputBlockId = &blockId;
+  }
+
   // Make sure the move is large enough to not be handled as a tap
-  nsEventStatus status = ApzcDown(aApzc, 10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE, aTime);
+  nsEventStatus status = ApzcDown(aApzc, 10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE, aTime, aOutInputBlockId);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[0] = status;
   }
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
 
   // Allowed touch behaviours must be set after sending touch-start.
   if (gfxPrefs::TouchActionEnabled() && aAllowedTouchBehaviors) {
-    aApzc->SetAllowedTouchBehavior(*aAllowedTouchBehaviors);
+    aApzc->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
   }
 
   MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY), ScreenSize(0, 0), 0, 0));
-  status = aApzc->ReceiveInputEvent(mti);
+  status = aApzc->ReceiveInputEvent(mti, nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[1] = status;
   }
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
 
   mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
-  status = aApzc->ReceiveInputEvent(mti);
+  status = aApzc->ReceiveInputEvent(mti, nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[2] = status;
   }
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
 
   if (!aKeepFingerDown) {
     status = ApzcUp(aApzc, 10, aTouchEndY, aTime);
@@ -342,20 +350,21 @@ ApzcPan(AsyncPanZoomController* aApzc,
  * consumed them and triggered scrolling behavior.
  */
 static void
 ApzcPanAndCheckStatus(AsyncPanZoomController* aApzc,
                       int& aTime,
                       int aTouchStartY,
                       int aTouchEndY,
                       bool aExpectConsumed,
-                      nsTArray<uint32_t>* aAllowedTouchBehaviors)
+                      nsTArray<uint32_t>* aAllowedTouchBehaviors,
+                      uint64_t* aOutInputBlockId = nullptr)
 {
   nsEventStatus statuses[4]; // down, move, move, up
-  ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses);
+  ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses, aOutInputBlockId);
 
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
 
   nsEventStatus touchMoveStatus;
   if (aExpectConsumed) {
     touchMoveStatus = nsEventStatus_eConsumeDoDefault;
   } else {
     touchMoveStatus = nsEventStatus_eIgnore;
@@ -363,19 +372,20 @@ ApzcPanAndCheckStatus(AsyncPanZoomContro
   EXPECT_EQ(touchMoveStatus, statuses[1]);
   EXPECT_EQ(touchMoveStatus, statuses[2]);
 }
 
 static void
 ApzcPanNoFling(AsyncPanZoomController* aApzc,
                int& aTime,
                int aTouchStartY,
-               int aTouchEndY)
+               int aTouchEndY,
+               uint64_t* aOutInputBlockId = nullptr)
 {
-  ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY);
+  ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY, false, nullptr, nullptr, aOutInputBlockId);
   aApzc->CancelAnimation();
 }
 
 static void
 ApzcPinchWithPinchInput(AsyncPanZoomController* aApzc,
                         int aFocusX, int aFocusY, float aScale,
                         nsEventStatus (*aOutEventStatuses)[3] = nullptr)
 {
@@ -419,55 +429,63 @@ ApzcPinchWithPinchInputAndCheckStatus(As
   EXPECT_EQ(expectedStatus, statuses[1]);
 }
 
 static void
 ApzcPinchWithTouchInput(AsyncPanZoomController* aApzc,
                         int aFocusX, int aFocusY, float aScale,
                         int& inputId,
                         nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
-                        nsEventStatus (*aOutEventStatuses)[4] = nullptr)
+                        nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+                        uint64_t* aOutInputBlockId = nullptr)
 {
   // Having pinch coordinates in float type may cause problems with high-precision scale values
   // since SingleTouchData accepts integer value. But for trivial tests it should be ok.
   float pinchLength = 100.0;
   float pinchLengthScaled = pinchLength * aScale;
 
+  // Even if the caller doesn't care about the block id, we need it to set the
+  // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
+  uint64_t blockId;
+  if (!aOutInputBlockId) {
+    aOutInputBlockId = &blockId;
+  }
+
   MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
   mtiStart.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX, aFocusY), ScreenSize(0, 0), 0, 0));
   mtiStart.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX, aFocusY), ScreenSize(0, 0), 0, 0));
-  nsEventStatus status = aApzc->ReceiveInputEvent(mtiStart);
+  nsEventStatus status = aApzc->ReceiveInputEvent(mtiStart, aOutInputBlockId);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[0] = status;
   }
 
   if (gfxPrefs::TouchActionEnabled() && aAllowedTouchBehaviors) {
-    aApzc->SetAllowedTouchBehavior(*aAllowedTouchBehaviors);
+    aApzc->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
   }
 
   MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
   mtiMove1.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLength, aFocusY), ScreenSize(0, 0), 0, 0));
   mtiMove1.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLength, aFocusY), ScreenSize(0, 0), 0, 0));
-  status = aApzc->ReceiveInputEvent(mtiMove1);
+  status = aApzc->ReceiveInputEvent(mtiMove1, nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[1] = status;
   }
 
   MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
   mtiMove2.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
   mtiMove2.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
-  status = aApzc->ReceiveInputEvent(mtiMove2);
+  status = aApzc->ReceiveInputEvent(mtiMove2, nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[2] = status;
   }
 
   MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
   mtiEnd.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
   mtiEnd.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
-  status = aApzc->ReceiveInputEvent(mtiEnd);
+  status = aApzc->ReceiveInputEvent(mtiEnd, nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[3] = status;
   }
 
   inputId += 2;
 }
 
 static void
@@ -613,20 +631,21 @@ TEST_F(APZCPinchGestureDetectorTester, P
 TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) {
   FrameMetrics originalMetrics = GetPinchableFrameMetrics();
   apzc->SetFrameMetrics(originalMetrics);
 
   SetMayHaveTouchListeners();
   MakeApzcZoomable();
 
   int touchInputId = 0;
-  ApzcPinchWithTouchInput(apzc, 250, 300, 1.25, touchInputId);
+  uint64_t blockId = 0;
+  ApzcPinchWithTouchInput(apzc, 250, 300, 1.25, touchInputId, nullptr, nullptr, &blockId);
 
   // Send the prevent-default notification for the touch block
-  apzc->ContentReceivedTouch(true);
+  apzc->ContentReceivedTouch(blockId, true);
 
   // Run all pending tasks (this should include at least the
   // prevent-default timer).
   EXPECT_LE(1, mcc->RunThroughDelayedTasks());
 
   // verify the metrics didn't change (i.e. the pinch was ignored)
   FrameMetrics fm = apzc->GetFrameMetrics();
   EXPECT_EQ(originalMetrics.GetZoom().scale, fm.GetZoom().scale);
@@ -817,25 +836,26 @@ protected:
   {
     SetMayHaveTouchListeners();
 
     int time = 0;
     int touchStart = 50;
     int touchEnd = 10;
     ScreenPoint pointOut;
     ViewTransform viewTransformOut;
+    uint64_t blockId = 0;
 
     // Pan down
     nsTArray<uint32_t> allowedTouchBehaviors;
     allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
-    ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, true, &allowedTouchBehaviors);
+    ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, true, &allowedTouchBehaviors, &blockId);
 
     // Send the signal that content has handled and preventDefaulted the touch
     // events. This flushes the event queue.
-    apzc->ContentReceivedTouch(true);
+    apzc->ContentReceivedTouch(blockId, true);
     // Run all pending tasks (this should include at least the
     // prevent-default timer).
     EXPECT_LE(1, mcc->RunThroughDelayedTasks());
 
     apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
     EXPECT_EQ(ScreenPoint(), pointOut);
     EXPECT_EQ(ViewTransform(), viewTransformOut);
 
@@ -1105,40 +1125,41 @@ protected:
   }
 
   void DoFlingStopWithSlowListener(bool aPreventDefault) {
     SetMayHaveTouchListeners();
 
     int time = 0;
     int touchStart = 50;
     int touchEnd = 10;
+    uint64_t blockId = 0;
 
     // Start the fling down.
-    ApzcPan(apzc, time, touchStart, touchEnd);
-    apzc->ContentReceivedTouch(false);
+    ApzcPan(apzc, time, touchStart, touchEnd, false, nullptr, nullptr, &blockId);
+    apzc->ContentReceivedTouch(blockId, false);
     while (mcc->RunThroughDelayedTasks());
 
     // Sample the fling a couple of times to ensure it's going.
     ScreenPoint point, finalPoint;
     ViewTransform viewTransform;
     apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(10), &viewTransform, point);
     apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(20), &viewTransform, finalPoint);
     EXPECT_GT(finalPoint.y, point.y);
 
     // Now we put our finger down to stop the fling
-    ApzcDown(apzc, 10, 10, time);
+    ApzcDown(apzc, 10, 10, time, &blockId);
 
     // Re-sample to make sure it hasn't moved
     apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(30), &viewTransform, point);
     EXPECT_EQ(finalPoint.x, point.x);
     EXPECT_EQ(finalPoint.y, point.y);
 
     // respond to the touchdown that stopped the fling.
     // even if we do a prevent-default on it, the animation should remain stopped.
-    apzc->ContentReceivedTouch(aPreventDefault);
+    apzc->ContentReceivedTouch(blockId, aPreventDefault);
     while (mcc->RunThroughDelayedTasks());
 
     // Verify the page hasn't moved
     apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(100), &viewTransform, point);
     EXPECT_EQ(finalPoint.x, point.x);
     EXPECT_EQ(finalPoint.y, point.y);
 
     // clean up
@@ -1206,36 +1227,38 @@ TEST_F(APZCGestureDetectorTester, Medium
 }
 
 class APZCLongPressTester : public APZCGestureDetectorTester {
 protected:
   void DoLongPressTest(uint32_t aBehavior) {
     MakeApzcUnzoomable();
 
     int time = 0;
-
-    nsEventStatus status = ApzcDown(apzc, 10, 10, time);
+    uint64_t blockId = 0;
+
+    nsEventStatus status = ApzcDown(apzc, 10, 10, time, &blockId);
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
     if (gfxPrefs::TouchActionEnabled()) {
       // SetAllowedTouchBehavior() must be called after sending touch-start.
       nsTArray<uint32_t> allowedTouchBehaviors;
       allowedTouchBehaviors.AppendElement(aBehavior);
-      apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
+      apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
     }
     // Have content "respond" to the touchstart
-    apzc->ContentReceivedTouch(false);
+    apzc->ContentReceivedTouch(blockId, false);
 
     MockFunction<void(std::string checkPointName)> check;
 
     {
       InSequence s;
 
       EXPECT_CALL(check, Call("preHandleLongTap"));
-      EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+      blockId++;
+      EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1);
       EXPECT_CALL(check, Call("postHandleLongTap"));
 
       EXPECT_CALL(check, Call("preHandleLongTapUp"));
       EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
       EXPECT_CALL(check, Call("postHandleLongTapUp"));
     }
 
     // There is a longpress event scheduled on a timeout
@@ -1249,17 +1272,17 @@ protected:
     // Destroy pending MAX_TAP timeout task
     mcc->DestroyOldestTask();
 
     // Dispatching the longpress event starts a new touch block, which
     // needs a new content response and also has a pending timeout task
     // in the queue. Deal with those here. We do the content response first
     // with preventDefault=false, and then we run the timeout task which
     // "loses the race" and does nothing.
-    apzc->ContentReceivedTouch(false);
+    apzc->ContentReceivedTouch(blockId, false);
     mcc->CheckHasDelayedTask();
     mcc->RunDelayedTask();
 
     time += 1000;
 
     // Finally, simulate lifting the finger. Since the long-press wasn't
     // prevent-defaulted, we should get a long-tap-up event.
     check.Call("preHandleLongTapUp");
@@ -1276,35 +1299,37 @@ protected:
     EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(0);
     EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
 
     int touchX = 10,
         touchStartY = 10,
         touchEndY = 50;
 
     int time = 0;
-    nsEventStatus status = ApzcDown(apzc, touchX, touchStartY, time);
+    uint64_t blockId = 0;
+    nsEventStatus status = ApzcDown(apzc, touchX, touchStartY, time, &blockId);
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
     if (gfxPrefs::TouchActionEnabled()) {
       // SetAllowedTouchBehavior() must be called after sending touch-start.
       nsTArray<uint32_t> allowedTouchBehaviors;
       allowedTouchBehaviors.AppendElement(aBehavior);
-      apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
+      apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
     }
     // Have content "respond" to the touchstart
-    apzc->ContentReceivedTouch(false);
+    apzc->ContentReceivedTouch(blockId, false);
 
     MockFunction<void(std::string checkPointName)> check;
 
     {
       InSequence s;
 
       EXPECT_CALL(check, Call("preHandleLongTap"));
-      EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(touchX, touchStartY), 0, apzc->GetGuid())).Times(1);
+      blockId++;
+      EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(touchX, touchStartY), 0, apzc->GetGuid(), blockId)).Times(1);
       EXPECT_CALL(check, Call("postHandleLongTap"));
     }
 
     mcc->CheckHasDelayedTask();
 
     // Manually invoke the longpress while the touch is currently down.
     check.Call("preHandleLongTap");
     mcc->RunDelayedTask();
@@ -1313,25 +1338,25 @@ protected:
     // Destroy pending MAX_TAP timeout task
     mcc->DestroyOldestTask();
 
     // There should be a TimeoutContentResponse task in the queue still,
     // waiting for the response from the longtap event dispatched above.
     // Send the signal that content has handled the long-tap, and then run
     // the timeout task (it will be a no-op because the content "wins" the
     // race. This takes the place of the "contextmenu" event.
-    apzc->ContentReceivedTouch(true);
+    apzc->ContentReceivedTouch(blockId, true);
     mcc->CheckHasDelayedTask();
     mcc->RunDelayedTask();
 
     time += 1000;
 
     MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, time, TimeStamp(), 0);
     mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
-    status = apzc->ReceiveInputEvent(mti);
+    status = apzc->ReceiveInputEvent(mti, nullptr);
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
     EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(0);
     status = ApzcUp(apzc, touchX, touchEndY, time);
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
     ScreenPoint pointOut;
     ViewTransform viewTransformOut;
@@ -1363,120 +1388,133 @@ TEST_F(APZCLongPressTester, LongPressPre
   SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
   DoLongPressPreventDefaultTest(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
                                 | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
                                 | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
 }
 
 static void
 ApzcDoubleTap(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime,
-              nsEventStatus (*aOutEventStatuses)[4] = nullptr)
+              nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+              uint64_t (*aOutInputBlockIds)[2] = nullptr)
 {
-  nsEventStatus status = ApzcDown(aApzc, aX, aY, aTime);
+  uint64_t blockId;
+  nsEventStatus status = ApzcDown(aApzc, aX, aY, aTime, &blockId);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[0] = status;
   }
+  if (aOutInputBlockIds) {
+    (*aOutInputBlockIds)[0] = blockId;
+  }
   aTime += 10;
   status = ApzcUp(aApzc, aX, aY, aTime);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[1] = status;
   }
   aTime += 10;
-  status = ApzcDown(aApzc, aX, aY, aTime);
+  status = ApzcDown(aApzc, aX, aY, aTime, &blockId);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[2] = status;
   }
+  if (aOutInputBlockIds) {
+    (*aOutInputBlockIds)[1] = blockId;
+  }
   aTime += 10;
   status = ApzcUp(aApzc, aX, aY, aTime);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[3] = status;
   }
 }
 
 static void
-ApzcDoubleTapAndCheckStatus(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime)
+ApzcDoubleTapAndCheckStatus(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime, uint64_t (*aOutInputBlockIds)[2] = nullptr)
 {
   nsEventStatus statuses[4];
-  ApzcDoubleTap(aApzc, aX, aY, aTime, &statuses);
+  ApzcDoubleTap(aApzc, aX, aY, aTime, &statuses, aOutInputBlockIds);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[3]);
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTap) {
   SetMayHaveTouchListeners();
   MakeApzcZoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
   EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
 
   int time = 0;
-  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
+  uint64_t blockIds[2];
+  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time, &blockIds);
 
   // responses to the two touchstarts
-  apzc->ContentReceivedTouch(false);
-  apzc->ContentReceivedTouch(false);
+  apzc->ContentReceivedTouch(blockIds[0], false);
+  apzc->ContentReceivedTouch(blockIds[1], false);
 
   while (mcc->RunThroughDelayedTasks());
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
   SetMayHaveTouchListeners();
   MakeApzcUnzoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(2);
   EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
 
   int time = 0;
-  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
+  uint64_t blockIds[2];
+  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time, &blockIds);
 
   // responses to the two touchstarts
-  apzc->ContentReceivedTouch(false);
-  apzc->ContentReceivedTouch(false);
+  apzc->ContentReceivedTouch(blockIds[0], false);
+  apzc->ContentReceivedTouch(blockIds[1], false);
 
   while (mcc->RunThroughDelayedTasks());
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) {
   SetMayHaveTouchListeners();
   MakeApzcZoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
 
   int time = 0;
-  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
+  uint64_t blockIds[2];
+  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time, &blockIds);
 
   // responses to the two touchstarts
-  apzc->ContentReceivedTouch(true);
-  apzc->ContentReceivedTouch(false);
+  apzc->ContentReceivedTouch(blockIds[0], true);
+  apzc->ContentReceivedTouch(blockIds[1], false);
 
   while (mcc->RunThroughDelayedTasks());
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
   SetMayHaveTouchListeners();
   MakeApzcZoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
   EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
 
   int time = 0;
-  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
+  uint64_t blockIds[2];
+  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time, &blockIds);
+printf_stderr("blockids %llu %llu\n", blockIds[0], blockIds[1]);
 
   // responses to the two touchstarts
-  apzc->ContentReceivedTouch(true);
-  apzc->ContentReceivedTouch(true);
+  apzc->ContentReceivedTouch(blockIds[0], true);
+  apzc->ContentReceivedTouch(blockIds[1], true);
 
   while (mcc->RunThroughDelayedTasks());
 
   apzc->AssertStateIsReset();
 }
 
 // Test for bug 947892
 // We test whether we dispatch tap event when the tap is followed by pinch.
@@ -1488,22 +1526,22 @@ TEST_F(APZCGestureDetectorTester, TapFol
   int time = 0;
   ApzcTap(apzc, 10, 10, time, 100);
 
   int inputId = 0;
   MultiTouchInput mti;
   mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
   mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
-  apzc->ReceiveInputEvent(mti);
+  apzc->ReceiveInputEvent(mti, nullptr);
 
   mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, time, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
   mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
-  apzc->ReceiveInputEvent(mti);
+  apzc->ReceiveInputEvent(mti, nullptr);
 
   while (mcc->RunThroughDelayedTasks());
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) {
   MakeApzcZoomable();
@@ -1512,27 +1550,27 @@ TEST_F(APZCGestureDetectorTester, TapFol
 
   int time = 0;
   ApzcTap(apzc, 10, 10, time, 100);
 
   int inputId = 0;
   MultiTouchInput mti;
   mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
-  apzc->ReceiveInputEvent(mti);
+  apzc->ReceiveInputEvent(mti, nullptr);
 
   mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
   mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
-  apzc->ReceiveInputEvent(mti);
+  apzc->ReceiveInputEvent(mti, nullptr);
 
   mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, time, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
   mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
-  apzc->ReceiveInputEvent(mti);
+  apzc->ReceiveInputEvent(mti, nullptr);
 
   while (mcc->RunThroughDelayedTasks());
 
   apzc->AssertStateIsReset();
 }
 
 class APZCTreeManagerTester : public ::testing::Test {
 protected:
@@ -1631,36 +1669,36 @@ ApzctmPan(APZCTreeManager* aTreeManager,
   SCOPED_GFX_PREF(APZTouchStartTolerance, float, 1.0f / 1000.0f);
   const int OVERCOME_TOUCH_TOLERANCE = 1;
 
   const int TIME_BETWEEN_TOUCH_EVENT = 100;
 
   // Make sure the move is large enough to not be handled as a tap
   MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY), ScreenSize(0, 0), 0, 0));
-  aTreeManager->ReceiveInputEvent(mti, nullptr);
+  aTreeManager->ReceiveInputEvent(mti, nullptr, nullptr);
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
 
   mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE), ScreenSize(0, 0), 0, 0));
-  aTreeManager->ReceiveInputEvent(mti, nullptr);
+  aTreeManager->ReceiveInputEvent(mti, nullptr, nullptr);
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
 
   mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
-  aTreeManager->ReceiveInputEvent(mti, nullptr);
+  aTreeManager->ReceiveInputEvent(mti, nullptr, nullptr);
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
 
   if (!aKeepFingerDown) {
     mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
     mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
-    aTreeManager->ReceiveInputEvent(mti, nullptr);
+    aTreeManager->ReceiveInputEvent(mti, nullptr, nullptr);
   }
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
 }
 
 class APZHitTestingTester : public APZCTreeManagerTester {
 protected:
   Matrix4x4 transformToApzc;
@@ -2043,45 +2081,45 @@ TEST_F(APZHitTestingTester, TestRepaintF
   // This first pan will move the APZC by 50 pixels, and dispatch a paint request.
   ApzcPanNoFling(apzcroot, time, 100, 50);
 
   // Verify that a touch start doesn't get untransformed
   ScreenIntPoint touchPoint(50, 50);
   MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0));
 
-  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr));
+  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
   EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
   check.Call("post-first-touch-start");
 
   // Send a touchend to clear state
   mti.mType = MultiTouchInput::MULTITOUCH_END;
-  manager->ReceiveInputEvent(mti, nullptr);
+  manager->ReceiveInputEvent(mti, nullptr, nullptr);
 
   AsyncPanZoomController::SetFrameTime(testStartTime + TimeDuration::FromMilliseconds(1000));
 
   // Now do two pans. The first of these will dispatch a repaint request, as above.
   // The second will get stuck in the paint throttler because the first one doesn't
   // get marked as "completed", so this will result in a non-empty LD transform.
   // (Note that any outstanding repaint requests from the first half of this test
   // don't impact this half because we advance the time by 1 second, which will trigger
   // the max-wait-exceeded codepath in the paint throttler).
   ApzcPanNoFling(apzcroot, time, 100, 50);
   check.Call("post-second-fling");
   ApzcPanNoFling(apzcroot, time, 100, 50);
 
   // Ensure that a touch start again doesn't get untransformed by flushing
   // a repaint
   mti.mType = MultiTouchInput::MULTITOUCH_START;
-  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr));
+  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
   EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
   check.Call("post-second-touch-start");
 
   mti.mType = MultiTouchInput::MULTITOUCH_END;
-  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr));
+  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
   EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
 
   mcc->RunThroughDelayedTasks();
 }
 
 class APZOverscrollHandoffTester : public APZCTreeManagerTester {
 protected:
   UniquePtr<ScopedLayerTreeRegistration> registration;
@@ -2175,20 +2213,21 @@ TEST_F(APZOverscrollHandoffTester, Defer
   TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
 
   // Enable touch-listeners so that we can separate the queueing of input
   // events from them being processed.
   childApzc->GetFrameMetrics().SetMayHaveTouchListeners(true);
 
   // Queue input events for a pan.
   int time = 0;
-  ApzcPanNoFling(childApzc, time, 90, 30);
+  uint64_t blockId = 0;
+  ApzcPanNoFling(childApzc, time, 90, 30, &blockId);
 
   // Allow the pan to be processed.
-  childApzc->ContentReceivedTouch(false);
+  childApzc->ContentReceivedTouch(blockId, false);
 
   // Make sure overscroll was handed off correctly.
   EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
   EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
 }
 
 // Here we test that if the layer structure changes in between two input
 // blocks being queued, and the first block is only processed after the second
@@ -2202,39 +2241,41 @@ TEST_F(APZOverscrollHandoffTester, Layer
   TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
 
   // Enable touch-listeners so that we can separate the queueing of input
   // events from them being processed.
   childApzc->GetFrameMetrics().SetMayHaveTouchListeners(true);
 
   // Queue input events for a pan.
   int time = 0;
-  ApzcPanNoFling(childApzc, time, 90, 30);
+  uint64_t blockId = 0;
+  ApzcPanNoFling(childApzc, time, 90, 30, &blockId);
 
   // Modify the APZC tree to insert a new APZC 'middle' into the handoff chain
   // between the child and the root.
   CreateOverscrollHandoffLayerTree2();
   nsRefPtr<Layer> middle = layers[1];
   childApzc->GetFrameMetrics().SetMayHaveTouchListeners(true);
   TestAsyncPanZoomController* middleApzc = ApzcOf(middle);
 
   // Queue input events for another pan.
-  ApzcPanNoFling(childApzc, time, 30, 90);
+  uint64_t secondBlockId = 0;
+  ApzcPanNoFling(childApzc, time, 30, 90, &secondBlockId);
 
   // Allow the first pan to be processed.
-  childApzc->ContentReceivedTouch(false);
+  childApzc->ContentReceivedTouch(blockId, false);
 
   // Make sure things have scrolled according to the handoff chain in
   // place at the time the touch-start of the first pan was queued.
   EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
   EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
   EXPECT_EQ(0, middleApzc->GetFrameMetrics().GetScrollOffset().y);
 
   // Allow the second pan to be processed.
-  childApzc->ContentReceivedTouch(false);
+  childApzc->ContentReceivedTouch(secondBlockId, false);
 
   // Make sure things have scrolled according to the handoff chain in
   // place at the time the touch-start of the second pan was queued.
   EXPECT_EQ(0, childApzc->GetFrameMetrics().GetScrollOffset().y);
   EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
   EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetScrollOffset().y);
 }
 
@@ -2254,22 +2295,22 @@ TEST_F(APZOverscrollHandoffTester, Stuck
   EXPECT_FALSE(child->IsOverscrolled());
   EXPECT_TRUE(rootApzc->IsOverscrolled());
 
   // Put a second finger down.
   MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
   // Use the same touch identifier for the first touch (0) as ApzctmPan(). (A bit hacky.)
   secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
   secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
-  manager->ReceiveInputEvent(secondFingerDown, nullptr);
+  manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
 
   // Release the fingers.
   MultiTouchInput fingersUp = secondFingerDown;
   fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
-  manager->ReceiveInputEvent(fingersUp, nullptr);
+  manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
 
   // Allow any animations to run their course.
   child->AdvanceAnimationsUntilEnd(testStartTime);
   rootApzc->AdvanceAnimationsUntilEnd(testStartTime);
 
   // Make sure nothing is overscrolled.
   EXPECT_FALSE(child->IsOverscrolled());
   EXPECT_FALSE(rootApzc->IsOverscrolled());
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -149,30 +149,31 @@ public:
     if (mRenderFrame) {
       TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
       browser->HandleSingleTap(aPoint, aModifiers, aGuid);
     }
   }
 
   virtual void HandleLongTap(const CSSPoint& aPoint,
                              int32_t aModifiers,
-                             const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE
+                             const ScrollableLayerGuid& aGuid,
+                             uint64_t aInputBlockId) MOZ_OVERRIDE
   {
     if (MessageLoop::current() != mUILoop) {
       // We have to send this message from the "UI thread" (main
       // thread).
       mUILoop->PostTask(
         FROM_HERE,
         NewRunnableMethod(this, &RemoteContentController::HandleLongTap,
-                          aPoint, aModifiers, aGuid));
+                          aPoint, aModifiers, aGuid, aInputBlockId));
       return;
     }
     if (mRenderFrame) {
       TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
-      browser->HandleLongTap(aPoint, aModifiers, aGuid);
+      browser->HandleLongTap(aPoint, aModifiers, aGuid, aInputBlockId);
     }
   }
 
   virtual void HandleLongTapUp(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE
   {
     if (MessageLoop::current() != mUILoop) {
@@ -411,20 +412,22 @@ RenderFrameParent::OwnerContentChanged(n
     ClientLayerManager *clientManager =
       static_cast<ClientLayerManager*>(lm.get());
     clientManager->GetRemoteRenderer()->SendAdoptChild(mLayersId);
   }
 }
 
 nsEventStatus
 RenderFrameParent::NotifyInputEvent(WidgetInputEvent& aEvent,
-                                    ScrollableLayerGuid* aOutTargetGuid)
+                                    ScrollableLayerGuid* aOutTargetGuid,
+                                    uint64_t* aOutInputBlockId)
 {
   if (GetApzcTreeManager()) {
-    return GetApzcTreeManager()->ReceiveInputEvent(aEvent, aOutTargetGuid);
+    return GetApzcTreeManager()->ReceiveInputEvent(
+        aEvent, aOutTargetGuid, aOutInputBlockId);
   }
   return nsEventStatus_eIgnore;
 }
 
 void
 RenderFrameParent::ActorDestroy(ActorDestroyReason why)
 {
   if (mLayersId != 0) {
@@ -523,25 +526,26 @@ RenderFrameParent::ZoomToRect(uint32_t a
   if (GetApzcTreeManager()) {
     GetApzcTreeManager()->ZoomToRect(ScrollableLayerGuid(mLayersId, aPresShellId, aViewId),
                                      aRect);
   }
 }
 
 void
 RenderFrameParent::ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
+                                        uint64_t aInputBlockId,
                                         bool aPreventDefault)
 {
   if (aGuid.mLayersId != mLayersId) {
     // Guard against bad data from hijacked child processes
     NS_ERROR("Unexpected layers id in ContentReceivedTouch; dropping message...");
     return;
   }
   if (GetApzcTreeManager()) {
-    GetApzcTreeManager()->ContentReceivedTouch(aGuid, aPreventDefault);
+    GetApzcTreeManager()->ContentReceivedTouch(aGuid, aInputBlockId, aPreventDefault);
   }
 }
 
 void
 RenderFrameParent::UpdateZoomConstraints(uint32_t aPresShellId,
                                          ViewID aViewId,
                                          bool aIsRoot,
                                          const ZoomConstraints& aConstraints)
--- a/layout/ipc/RenderFrameParent.h
+++ b/layout/ipc/RenderFrameParent.h
@@ -83,23 +83,28 @@ public:
   /**
    * Notify the APZ code of an input event, and get back the untransformed event.
    * @param aEvent the input event; this is modified in-place so that the async
    *        transforms are unapplied. This can be passed to Gecko for hit testing
    *        and normal event dispatch.
    * @param aOutTargetGuid An out-parameter that will contain the identifier
    *        of the APZC instance that handled the event, if one was found. This
    *        argument may be null.
+   * @param aOutInputBlockId An out-parameter that will contain the identifier
+   *        of the input block that this event was added to, if there was on.
+   *        This argument may be null.
    */
   nsEventStatus NotifyInputEvent(WidgetInputEvent& aEvent,
-                                 ScrollableLayerGuid* aOutTargetGuid);
+                                 ScrollableLayerGuid* aOutTargetGuid,
+                                 uint64_t* aOutInputBlockId);
 
   void ZoomToRect(uint32_t aPresShellId, ViewID aViewId, const CSSRect& aRect);
 
   void ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
+                            uint64_t aInputBlockId,
                             bool aPreventDefault);
 
   void UpdateZoomConstraints(uint32_t aPresShellId,
                              ViewID aViewId,
                              bool aIsRoot,
                              const ZoomConstraints& aConstraints);
 
   bool HitTest(const nsRect& aRect);
--- a/widget/android/APZCCallbackHandler.cpp
+++ b/widget/android/APZCCallbackHandler.cpp
@@ -32,32 +32,33 @@ APZCCallbackHandler::SetNativePanZoomCon
 {
     NativePanZoomController* old = mNativePanZoomController;
     mNativePanZoomController = NativePanZoomController::Wrap(obj);
     return old;
 }
 
 void
 APZCCallbackHandler::NotifyDefaultPrevented(const ScrollableLayerGuid& aGuid,
+                                            uint64_t aInputBlockId,
                                             bool aDefaultPrevented)
 {
     if (!AndroidBridge::IsJavaUiThread()) {
         // The notification must reach the APZ on the Java UI thread (aka the
         // APZ "controller" thread) but we get it from the Gecko thread, so we
         // have to throw it onto the other thread.
         AndroidBridge::Bridge()->PostTaskToUiThread(NewRunnableMethod(
             this, &APZCCallbackHandler::NotifyDefaultPrevented,
-            aGuid, aDefaultPrevented), 0);
+            aGuid, aInputBlockId, aDefaultPrevented), 0);
         return;
     }
 
     MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
     APZCTreeManager* controller = nsWindow::GetAPZCTreeManager();
     if (controller) {
-        controller->ContentReceivedTouch(aGuid, aDefaultPrevented);
+        controller->ContentReceivedTouch(aGuid, aInputBlockId, aDefaultPrevented);
     }
 }
 
 nsIDOMWindowUtils*
 APZCCallbackHandler::GetDOMWindowUtils()
 {
     nsIAndroidBrowserApp* browserApp = nullptr;
     if (!nsAppShell::gAppShell) {
@@ -132,17 +133,18 @@ APZCCallbackHandler::HandleSingleTap(con
     nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", point.x, point.y);
     nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
             NS_LITERAL_CSTRING("Gesture:SingleTap"), data));
 }
 
 void
 APZCCallbackHandler::HandleLongTap(const CSSPoint& aPoint,
                                    int32_t aModifiers,
-                                   const mozilla::layers::ScrollableLayerGuid& aGuid)
+                                   const mozilla::layers::ScrollableLayerGuid& aGuid,
+                                   uint64_t aInputBlockId)
 {
     // TODO send content response back to APZC
     CSSIntPoint point = RoundedToInt(aPoint);
     nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", point.x, point.y);
     nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
             NS_LITERAL_CSTRING("Gesture:LongPress"), data));
 }
 
--- a/widget/android/APZCCallbackHandler.h
+++ b/widget/android/APZCCallbackHandler.h
@@ -33,28 +33,29 @@ public:
     static APZCCallbackHandler* GetInstance() {
         if (sInstance.get() == nullptr) {
             sInstance = new APZCCallbackHandler();
         }
         return sInstance.get();
     }
 
     NativePanZoomController* SetNativePanZoomController(jobject obj);
-    void NotifyDefaultPrevented(const mozilla::layers::ScrollableLayerGuid& aGuid, bool aDefaultPrevented);
+    void NotifyDefaultPrevented(const mozilla::layers::ScrollableLayerGuid& aGuid, uint64_t aInputBlockId, bool aDefaultPrevented);
 
 public: // GeckoContentController methods
     void RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
     void AcknowledgeScrollUpdate(const mozilla::layers::FrameMetrics::ViewID& aScrollId,
                                  const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
     void HandleDoubleTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
                          const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     void HandleSingleTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
                          const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     void HandleLongTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
-                       const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
+                       const mozilla::layers::ScrollableLayerGuid& aGuid,
+                       uint64_t aInputBlockId) MOZ_OVERRIDE;
     void HandleLongTapUp(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
                          const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect& aContentRect,
                                  const mozilla::CSSSize& aScrollableSize) MOZ_OVERRIDE;
     void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE;
 };
 
 } // namespace android
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -919,19 +919,20 @@ Java_org_mozilla_gecko_gfx_NativePanZoom
     MultiTouchInput input = wrapper->MakeMultiTouchInput(nsWindow::TopWindow());
     delete wrapper;
 
     if (input.mType < 0 || !nsAppShell::gAppShell) {
         return false;
     }
 
     ScrollableLayerGuid guid;
-    nsEventStatus status = controller->ReceiveInputEvent(input, &guid);
+    uint64_t blockId;
+    nsEventStatus status = controller->ReceiveInputEvent(input, &guid, &blockId);
     if (status != nsEventStatus_eConsumeNoDefault) {
-        nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeApzInputEvent(input, guid));
+        nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeApzInputEvent(input, guid, blockId));
     }
     return true;
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_handleMotionEvent(JNIEnv* env, jobject instance, jobject event)
 {
     // FIXME implement this
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -685,16 +685,23 @@ AndroidGeckoEvent::CanCoalesceWith(Andro
 
 mozilla::layers::ScrollableLayerGuid
 AndroidGeckoEvent::ApzGuid()
 {
     MOZ_ASSERT(Type() == APZ_INPUT_EVENT);
     return mApzGuid;
 }
 
+uint64_t
+AndroidGeckoEvent::ApzInputBlockId()
+{
+    MOZ_ASSERT(Type() == APZ_INPUT_EVENT);
+    return mApzInputBlockId;
+}
+
 WidgetTouchEvent
 AndroidGeckoEvent::MakeTouchEvent(nsIWidget* widget)
 {
     if (Type() == APZ_INPUT_EVENT) {
         return mApzInput.ToWidgetTouchEvent(widget);
     }
 
     int type = NS_EVENT_NULL;
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -482,21 +482,22 @@ public:
     static AndroidGeckoEvent* MakeAddObserver(const nsAString &key, nsIObserver *observer) {
         AndroidGeckoEvent *event = new AndroidGeckoEvent();
         event->Init(ADD_OBSERVER);
         event->mCharacters.Assign(key);
         event->mObserver = observer;
         return event;
     }
 
-    static AndroidGeckoEvent* MakeApzInputEvent(const MultiTouchInput& aInput, const mozilla::layers::ScrollableLayerGuid& aGuid) {
+    static AndroidGeckoEvent* MakeApzInputEvent(const MultiTouchInput& aInput, const mozilla::layers::ScrollableLayerGuid& aGuid, uint64_t aInputBlockId) {
         AndroidGeckoEvent* event = new AndroidGeckoEvent();
         event->Init(APZ_INPUT_EVENT);
         event->mApzInput = aInput;
         event->mApzGuid = aGuid;
+        event->mApzInputBlockId = aInputBlockId;
         return event;
     }
 
     int Action() { return mAction; }
     int Type() { return mType; }
     bool AckNeeded() { return mAckNeeded; }
     int64_t Time() { return mTime; }
     const nsTArray<nsIntPoint>& Points() { return mPoints; }
@@ -555,16 +556,17 @@ public:
     const AutoGlobalWrappedJavaObject& Object() { return mObject; }
     bool CanCoalesceWith(AndroidGeckoEvent* ae);
     WidgetTouchEvent MakeTouchEvent(nsIWidget* widget);
     MultiTouchInput MakeMultiTouchInput(nsIWidget* widget);
     WidgetMouseEvent MakeMouseEvent(nsIWidget* widget);
     void UnionRect(nsIntRect const& aRect);
     nsIObserver *Observer() { return mObserver; }
     mozilla::layers::ScrollableLayerGuid ApzGuid();
+    uint64_t ApzInputBlockId();
 
 protected:
     int mAction;
     int mType;
     bool mAckNeeded;
     int64_t mTime;
     nsTArray<nsIntPoint> mPoints;
     nsTArray<nsIntPoint> mPointRadii;
@@ -597,16 +599,17 @@ protected:
     int mGamepadButton;
     bool mGamepadButtonPressed;
     float mGamepadButtonValue;
     nsTArray<float> mGamepadValues;
     nsCOMPtr<nsIObserver> mObserver;
     nsTArray<nsString> mPrefNames;
     MultiTouchInput mApzInput;
     mozilla::layers::ScrollableLayerGuid mApzGuid;
+    uint64_t mApzInputBlockId;
     AutoGlobalWrappedJavaObject mObject;
 
     void ReadIntArray(nsTArray<int> &aVals,
                       JNIEnv *jenv,
                       jfieldID field,
                       int32_t count);
     void ReadFloatArray(nsTArray<float> &aVals,
                         JNIEnv *jenv,
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -1091,30 +1091,30 @@ bool nsWindow::OnMultitouchEvent(Android
     // if the last event we got was a down event, then by now we know for sure whether
     // this block has been default-prevented or not. if we haven't already sent the
     // notification for this block, do so now.
     if (sLastWasDownEvent && !sDefaultPreventedNotified) {
         // if this event is a down event, that means it's the start of a new block, and the
         // previous block should not be default-prevented
         bool defaultPrevented = isDownEvent ? false : preventDefaultActions;
         if (ae->Type() == AndroidGeckoEvent::APZ_INPUT_EVENT) {
-            mozilla::widget::android::APZCCallbackHandler::GetInstance()->NotifyDefaultPrevented(ae->ApzGuid(), defaultPrevented);
+            mozilla::widget::android::APZCCallbackHandler::GetInstance()->NotifyDefaultPrevented(ae->ApzGuid(), ae->ApzInputBlockId(), defaultPrevented);
         } else {
             mozilla::widget::android::GeckoAppShell::NotifyDefaultPrevented(defaultPrevented);
         }
         sDefaultPreventedNotified = true;
     }
 
     // now, if this event is a down event, then we might already know that it has been
     // default-prevented. if so, we send the notification right away; otherwise we wait
     // for the next event.
     if (isDownEvent) {
         if (preventDefaultActions) {
             if (ae->Type() == AndroidGeckoEvent::APZ_INPUT_EVENT) {
-                mozilla::widget::android::APZCCallbackHandler::GetInstance()->NotifyDefaultPrevented(ae->ApzGuid(), true);
+                mozilla::widget::android::APZCCallbackHandler::GetInstance()->NotifyDefaultPrevented(ae->ApzGuid(), ae->ApzInputBlockId(), true);
             } else {
                 mozilla::widget::android::GeckoAppShell::NotifyDefaultPrevented(true);
             }
             sDefaultPreventedNotified = true;
         } else {
             sDefaultPreventedNotified = false;
         }
     }
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -423,17 +423,18 @@ public:
     APZCCallbackHelper::AcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
   }
 
   virtual void HandleDoubleTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
   virtual void HandleSingleTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
   virtual void HandleLongTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
-                               const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
+                               const ScrollableLayerGuid& aGuid,
+                               uint64_t aInputBlockId) MOZ_OVERRIDE {}
   virtual void HandleLongTapUp(const CSSPoint& aPoint, int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
   virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect,
                                        const mozilla::CSSSize &aScrollableSize) MOZ_OVERRIDE {}
 };
 
 } // unnamed namespace
 
@@ -5369,67 +5370,67 @@ static int32_t RoundUp(double aDouble)
     // MayBegin and Cancelled are dispatched when the fingers start or stop
     // touching the touchpad before any scrolling has occurred. These events
     // can be used to control scrollbar visibility or interrupt scroll
     // animations. They are only dispatched on 10.8 or later, and only by
     // relatively modern devices.
     if (phase == NSEventPhaseMayBegin) {
       PanGestureInput panInput(PanGestureInput::PANGESTURE_MAYSTART, eventTime,
                                eventTimeStamp, location, ScreenPoint(0, 0), 0);
-      apzctm->ReceiveInputEvent(panInput, &guid);
+      apzctm->ReceiveInputEvent(panInput, &guid, nullptr);
       return;
     }
     if (phase == NSEventPhaseCancelled) {
       PanGestureInput panInput(PanGestureInput::PANGESTURE_CANCELLED, eventTime,
                                eventTimeStamp, location, ScreenPoint(0, 0), 0);
-      apzctm->ReceiveInputEvent(panInput, &guid);
+      apzctm->ReceiveInputEvent(panInput, &guid, nullptr);
       return;
     }
 
     // Legacy scroll events are dispatched by devices that do not have a
     // concept of a scroll gesture, for example by USB mice with
     // traditional mouse wheels.
     // For these kinds of scrolls, we want to surround every single scroll
     // event with a PANGESTURE_START and a PANGESTURE_END event. The APZC
     // needs to know that the real scroll gesture can end abruptly after any
     // one of these events.
     bool isLegacyScroll = (phase == NSEventPhaseNone &&
       momentumPhase == NSEventPhaseNone && delta != ScreenPoint(0, 0));
 
     if (phase == NSEventPhaseBegan || isLegacyScroll) {
       PanGestureInput panInput(PanGestureInput::PANGESTURE_START, eventTime,
                                eventTimeStamp, location, ScreenPoint(0, 0), 0);
-      apzctm->ReceiveInputEvent(panInput, &guid);
+      apzctm->ReceiveInputEvent(panInput, &guid, nullptr);
     }
     if (momentumPhase == NSEventPhaseNone && delta != ScreenPoint(0, 0)) {
       PanGestureInput panInput(PanGestureInput::PANGESTURE_PAN, eventTime,
                                eventTimeStamp, location, delta, 0);
-      apzctm->ReceiveInputEvent(panInput, &guid);
+      apzctm->ReceiveInputEvent(panInput, &guid, nullptr);
     }
     if (phase == NSEventPhaseEnded || isLegacyScroll) {
       PanGestureInput panInput(PanGestureInput::PANGESTURE_END, eventTime,
                                eventTimeStamp, location, ScreenPoint(0, 0), 0);
-      apzctm->ReceiveInputEvent(panInput, &guid);
+      apzctm->ReceiveInputEvent(panInput, &guid, nullptr);
     }
 
     // Any device that can dispatch momentum events supports all three momentum phases.
     if (momentumPhase == NSEventPhaseBegan) {
       PanGestureInput panInput(PanGestureInput::PANGESTURE_MOMENTUMSTART, eventTime,
                                eventTimeStamp, location, ScreenPoint(0, 0), 0);
-      apzctm->ReceiveInputEvent(panInput, &guid);
+      apzctm->ReceiveInputEvent(panInput, &guid, nullptr);
     }
     if (momentumPhase == NSEventPhaseChanged && delta != ScreenPoint(0, 0)) {
       PanGestureInput panInput(PanGestureInput::PANGESTURE_MOMENTUMPAN, eventTime,
                                eventTimeStamp, location, delta, 0);
-      apzctm->ReceiveInputEvent(panInput, &guid);
+      apzctm->ReceiveInputEvent(panInput, &guid, nullptr);
     }
     if (momentumPhase == NSEventPhaseEnded) {
       PanGestureInput panInput(PanGestureInput::PANGESTURE_MOMENTUMEND, eventTime,
                                eventTimeStamp, location, ScreenPoint(0, 0), 0);
-      apzctm->ReceiveInputEvent(panInput, &guid);
+      apzctm->ReceiveInputEvent(panInput, &guid, nullptr);
     }
   }
 }
 
 -(NSMenu*)menuForEvent:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
--- a/widget/gonk/ParentProcessController.h
+++ b/widget/gonk/ParentProcessController.h
@@ -26,17 +26,18 @@ public:
     virtual void HandleDoubleTap(const CSSPoint& aPoint,
                                  int32_t aModifiers,
                                  const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
     virtual void HandleSingleTap(const CSSPoint& aPoint,
                                  int32_t aModifiers,
                                  const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
     virtual void HandleLongTap(const CSSPoint& aPoint,
                                int32_t aModifiers,
-                               const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
+                               const ScrollableLayerGuid& aGuid,
+                               uint64_t aInputBlockId) MOZ_OVERRIDE {}
     virtual void HandleLongTapUp(const CSSPoint& aPoint,
                                  int32_t aModifiers,
                                  const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
 
     virtual void SendAsyncScrollDOMEvent(bool aIsRoot,
                                          const CSSRect &aContentRect,
                                          const CSSSize &aScrollableSize) MOZ_OVERRIDE {}
 };
--- a/widget/windows/winrt/APZController.cpp
+++ b/widget/windows/winrt/APZController.cpp
@@ -81,22 +81,22 @@ GetDOMTargets(uint64_t aScrollId,
 
 void
 APZController::SetPendingResponseFlusher(APZPendingResponseFlusher* aFlusher)
 {
   mFlusher = aFlusher;
 }
 
 void
-APZController::ContentReceivedTouch(const ScrollableLayerGuid& aGuid, bool aPreventDefault)
+APZController::ContentReceivedTouch(const ScrollableLayerGuid& aGuid, const uint64_t aInputBlockId, bool aPreventDefault)
 {
   if (!sAPZC) {
     return;
   }
-  sAPZC->ContentReceivedTouch(aGuid, aPreventDefault);
+  sAPZC->ContentReceivedTouch(aGuid, aInputBlockId, aPreventDefault);
 }
 
 bool
 APZController::HitTestAPZC(ScreenIntPoint& aPoint)
 {
   if (!sAPZC) {
     return false;
   }
@@ -110,24 +110,25 @@ APZController::TransformCoordinateToGeck
   if (!sAPZC || !aRefPointOut) {
     return;
   }
   sAPZC->TransformCoordinateToGecko(aPoint, aRefPointOut);
 }
 
 nsEventStatus
 APZController::ReceiveInputEvent(WidgetInputEvent* aEvent,
-                                 ScrollableLayerGuid* aOutTargetGuid)
+                                 ScrollableLayerGuid* aOutTargetGuid,
+                                 uint64_t* aOutInputBlockId)
 {
   MOZ_ASSERT(aEvent);
 
   if (!sAPZC) {
     return nsEventStatus_eIgnore;
   }
-  return sAPZC->ReceiveInputEvent(*aEvent->AsInputEvent(), aOutTargetGuid);
+  return sAPZC->ReceiveInputEvent(*aEvent->AsInputEvent(), aOutTargetGuid, aOutInputBlockId);
 }
 
 // APZC sends us this request when we need to update the display port on
 // the scrollable frame the apzc is managing.
 void
 APZController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
 {
 #ifdef DEBUG_CONTROLLER
@@ -209,22 +210,23 @@ APZController::HandleSingleTap(const CSS
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid)
 {
 }
 
 void
 APZController::HandleLongTap(const CSSPoint& aPoint,
                              int32_t aModifiers,
-                             const ScrollableLayerGuid& aGuid)
+                             const ScrollableLayerGuid& aGuid,
+                             uint64_t aInputBlockId)
 {
   if (mFlusher) {
     mFlusher->FlushPendingContentResponse();
   }
-  ContentReceivedTouch(aGuid, false);
+  ContentReceivedTouch(aGuid, aInputBlockId, false);
 }
 
 void
 APZController::HandleLongTapUp(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid)
 {
 }
--- a/widget/windows/winrt/APZController.h
+++ b/widget/windows/winrt/APZController.h
@@ -41,35 +41,37 @@ public:
   virtual void HandleDoubleTap(const mozilla::CSSPoint& aPoint,
                                int32_t aModifiers,
                                const mozilla::layers::ScrollableLayerGuid& aGuid);
   virtual void HandleSingleTap(const mozilla::CSSPoint& aPoint,
                                int32_t aModifiers,
                                const mozilla::layers::ScrollableLayerGuid& aGuid);
   virtual void HandleLongTap(const mozilla::CSSPoint& aPoint,
                              int32_t aModifiers,
-                             const mozilla::layers::ScrollableLayerGuid& aGuid);
+                             const mozilla::layers::ScrollableLayerGuid& aGuid,
+                             uint64_t aInputBlockId);
   virtual void HandleLongTapUp(const mozilla::CSSPoint& aPoint,
                                int32_t aModifiers,
                                const mozilla::layers::ScrollableLayerGuid& aGuid);
   virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize);
   virtual void PostDelayedTask(Task* aTask, int aDelayMs);
   virtual bool GetRootZoomConstraints(ZoomConstraints* aOutConstraints);
   virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                     APZStateChange aChange,
                                     int aArg);
 
   void SetPendingResponseFlusher(APZPendingResponseFlusher* aFlusher);
   
   bool HitTestAPZC(mozilla::ScreenIntPoint& aPoint);
   void TransformCoordinateToGecko(const mozilla::ScreenIntPoint& aPoint,
                                   LayoutDeviceIntPoint* aRefPointOut);
-  void ContentReceivedTouch(const ScrollableLayerGuid& aGuid, bool aPreventDefault);
+  void ContentReceivedTouch(const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId, bool aPreventDefault);
   nsEventStatus ReceiveInputEvent(mozilla::WidgetInputEvent* aEvent,
-                                  ScrollableLayerGuid* aOutTargetGuid);
+                                  ScrollableLayerGuid* aOutTargetGuid,
+                                  uint64_t* aOutInputBlockId);
 
 public:
   // todo: make this a member variable as prep for multiple views
   static nsRefPtr<mozilla::layers::APZCTreeManager> sAPZC;
 
 private:
   APZPendingResponseFlusher* mFlusher;
 };
--- a/widget/windows/winrt/MetroInput.cpp
+++ b/widget/windows/winrt/MetroInput.cpp
@@ -261,17 +261,18 @@ namespace winrt {
 
 MetroInput::InputPrecisionLevel MetroInput::sCurrentInputLevel =
   MetroInput::InputPrecisionLevel::LEVEL_IMPRECISE;
 
 MetroInput::MetroInput(MetroWidget* aWidget,
                        UI::Core::ICoreWindow* aWindow)
               : mWidget(aWidget),
                 mNonApzTargetForTouch(false),
-                mWindow(aWindow)
+                mWindow(aWindow),
+                mInputBlockId(0)
 {
   LogFunction();
   NS_ASSERTION(aWidget, "Attempted to create MetroInput for null widget!");
   NS_ASSERTION(aWindow, "Attempted to create MetroInput for null window!");
 
   mWidget->SetApzPendingResponseFlusher(this);
 
   Preferences::AddBoolVarCache(&gTouchActionPropertyEnabled, "layout.css.touch_action.enabled", gTouchActionPropertyEnabled);
@@ -1210,17 +1211,17 @@ static void DumpTouchBehavior(nsTArray<u
 
 void
 MetroInput::HandleTouchStartEvent(WidgetTouchEvent* aEvent)
 {
   // This is the start of a new touch block. See what the APZ wants to do with it.
 
   WidgetTouchEvent transformedEvent(*aEvent);
   DUMP_TOUCH_IDS("APZC(1)", aEvent);
-  nsEventStatus result = mWidget->ApzReceiveInputEvent(&transformedEvent, &mTargetAPZCGuid);
+  nsEventStatus result = mWidget->ApzReceiveInputEvent(&transformedEvent, &mTargetAPZCGuid, &mInputBlockId);
   if (result == nsEventStatus_eConsumeNoDefault) {
     // The APZ said: throw this event away entirely.
     CancelGesture();
     return;
   }
 
   // If the APZ is using this block, send it touch-action behavior.
   if (gTouchActionPropertyEnabled) {
@@ -1230,41 +1231,41 @@ MetroInput::HandleTouchStartEvent(Widget
     // from and we're setting to may changes if there are multiple touches (in that
     // case apzctm needs to take common ancestor of them).
     GetAllowedTouchBehavior(&transformedEvent, touchBehaviors);
     // Setting the touch behaviors to the apzc that will be responsible
     // for interpreting it. It may be not the same apzc we retrieved touch
     // action values from. E.g. for zooming we're taking parent apzc of a few ones
     // that were touched but touch behaviors would be taken from childs.
     DUMP_ALLOWED_TOUCH_BEHAVIOR(touchBehaviors);
-    mWidget->ApzcSetAllowedTouchBehavior(mTargetAPZCGuid, touchBehaviors);
+    mWidget->ApzcSetAllowedTouchBehavior(mTargetAPZCGuid, mInputBlockId, touchBehaviors);
   }
 
   // Pass the event on to content
   DUMP_TOUCH_IDS("DOM(2)", aEvent);
   nsEventStatus contentStatus = nsEventStatus_eIgnore;
   mWidget->DispatchEvent(&transformedEvent, contentStatus);
   if (nsEventStatus_eConsumeNoDefault == contentStatus) {
     // Content consumed the event, so we need to notify the APZ
     // to not do anything with this touch block.
-    mWidget->ApzContentConsumingTouch(mTargetAPZCGuid);
+    mWidget->ApzContentConsumingTouch(mTargetAPZCGuid, mInputBlockId);
     mCancelable = false;
 
     // Also cancel the gesture detection.
     CancelGesture();
   }
 }
 
 void
 MetroInput::HandleFirstTouchMoveEvent(WidgetTouchEvent* aEvent)
 {
   // If the APZ is using this block, pass the event to it.
   WidgetTouchEvent transformedEvent(*aEvent);
   DUMP_TOUCH_IDS("APZC(2)", aEvent);
-  nsEventStatus apzcStatus = mWidget->ApzReceiveInputEvent(&transformedEvent, &mTargetAPZCGuid);
+  nsEventStatus apzcStatus = mWidget->ApzReceiveInputEvent(&transformedEvent, &mTargetAPZCGuid, &mInputBlockId);
   if (apzcStatus == nsEventStatus_eConsumeNoDefault) {
     // The APZ said: throw this event away entirely.
     CancelGesture();
     return;
   }
 
   // ==== RANDOM INTERLUDE ABOut POINTER EVENTS ====
   // We need to dispatch here only touch event, not pointer one.
@@ -1284,19 +1285,19 @@ MetroInput::HandleFirstTouchMoveEvent(Wi
   // And pass the untransformed event to content.
   DUMP_TOUCH_IDS("DOM(3)", aEvent);
   nsEventStatus contentStatus = nsEventStatus_eIgnore;
   mWidget->DispatchEvent(&transformedEvent, contentStatus);
 
   // Let the apz know if content wants to consume touch events.
   if (mCancelable) {
     if (nsEventStatus_eConsumeNoDefault == contentStatus) {
-      mWidget->ApzContentConsumingTouch(mTargetAPZCGuid);
+      mWidget->ApzContentConsumingTouch(mTargetAPZCGuid, mInputBlockId);
     } else {
-      mWidget->ApzContentIgnoringTouch(mTargetAPZCGuid);
+      mWidget->ApzContentIgnoringTouch(mTargetAPZCGuid, mInputBlockId);
       if (apzcStatus == nsEventStatus_eConsumeDoDefault) {
         SendPointerCancelToContent(transformedEvent);
       }
     }
     mCancelable = false;
   }
 
   // Cancel the gesture detection as well if content is taking this block.
@@ -1322,17 +1323,17 @@ MetroInput::SendPointerCancelToContent(c
 }
 
 bool
 MetroInput::SendPendingResponseToApz()
 {
   // If this is called, content has missed its chance to consume this event block
   // so we should notify the APZ that content is ignoring this touch block.
   if (mCancelable) {
-    mWidget->ApzContentIgnoringTouch(mTargetAPZCGuid);
+    mWidget->ApzContentIgnoringTouch(mTargetAPZCGuid, mInputBlockId);
     mCancelable = false;
     return true;
   }
   return false;
 }
 
 void
 MetroInput::FlushPendingContentResponse()
@@ -1368,16 +1369,17 @@ MetroInput::DeliverNextQueuedTouchEvent(
   // This is the start of a new touch block. If we haven't
   // responded to the APZ about the last touch block, do that
   // now, and reset variables for the new touch block.
   if (event->message == NS_TOUCH_START) {
     SendPendingResponseToApz();
 
     mCancelable = true;
     mTargetAPZCGuid = ScrollableLayerGuid();
+    mInputBlockId = 0;
   }
 
   // Test for non-apz vs. apz target. To do this we only use the first touch
   // point since that will be the input batch target. Cache this for touch events
   // since HitTestChrome has to send a dom event.
   if (event->message == NS_TOUCH_START && event->touches.Length() == 1) {
     nsRefPtr<Touch> touch = event->touches[0];
     LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromUntyped(touch->mRefPoint);
@@ -1419,17 +1421,17 @@ MetroInput::DeliverNextQueuedTouchEvent(
   // If we get here, content has already had its chance to consume this event
   // block. If it didn't do so we can inform the APZ that content is ignoring
   // this event block.
   bool responseSent = SendPendingResponseToApz();
 
   // Normal processing of events. Send it to the APZ first for handling and
   // untransformation. then pass the untransformed event to content.
   DUMP_TOUCH_IDS("APZC(3)", event);
-  status = mWidget->ApzReceiveInputEvent(event, nullptr);
+  status = mWidget->ApzReceiveInputEvent(event, nullptr, nullptr);
   if (status == nsEventStatus_eConsumeNoDefault) {
     CancelGesture();
     return;
   }
   if (responseSent && status == nsEventStatus_eConsumeDoDefault) {
     SendPointerCancelToContent(*event);
     return;
   }
--- a/widget/windows/winrt/MetroInput.h
+++ b/widget/windows/winrt/MetroInput.h
@@ -281,12 +281,13 @@ private:
   bool SendPendingResponseToApz();
   void CancelGesture();
 
   // Sync event dispatching
   void DispatchEventIgnoreStatus(WidgetGUIEvent* aEvent);
 
   nsDeque mInputEventQueue;
   mozilla::layers::ScrollableLayerGuid mTargetAPZCGuid;
+  uint64_t mInputBlockId;
   static nsEventStatus sThrowawayStatus;
 };
 
 } } }
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -1069,43 +1069,44 @@ MetroWidget::ApzcGetAllowedTouchBehavior
                                          nsTArray<TouchBehaviorFlags>& aOutBehaviors)
 {
   LogFunction();
   return APZController::sAPZC->GetAllowedTouchBehavior(aTransformedEvent, aOutBehaviors);
 }
 
 void
 MetroWidget::ApzcSetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid,
+                                         uint64_t aInputBlockId,
                                          nsTArray<TouchBehaviorFlags>& aBehaviors)
 {
   LogFunction();
   if (!APZController::sAPZC) {
     return;
   }
-  APZController::sAPZC->SetAllowedTouchBehavior(aGuid, aBehaviors);
+  APZController::sAPZC->SetAllowedTouchBehavior(aGuid, aInputBlockId, aBehaviors);
 }
 
 void
-MetroWidget::ApzContentConsumingTouch(const ScrollableLayerGuid& aGuid)
+MetroWidget::ApzContentConsumingTouch(const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId)
 {
   LogFunction();
   if (!mController) {
     return;
   }
-  mController->ContentReceivedTouch(aGuid, true);
+  mController->ContentReceivedTouch(aGuid, aInputBlockId, true);
 }
 
 void
-MetroWidget::ApzContentIgnoringTouch(const ScrollableLayerGuid& aGuid)
+MetroWidget::ApzContentIgnoringTouch(const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId)
 {
   LogFunction();
   if (!mController) {
     return;
   }
-  mController->ContentReceivedTouch(aGuid, false);
+  mController->ContentReceivedTouch(aGuid, aInputBlockId, false);
 }
 
 bool
 MetroWidget::ApzHitTest(ScreenIntPoint& pt)
 {
   if (!mController) {
     return false;
   }
@@ -1119,24 +1120,25 @@ MetroWidget::ApzTransformGeckoCoordinate
   if (!mController) {
     return;
   }
   mController->TransformCoordinateToGecko(aPoint, aRefPointOut);
 }
 
 nsEventStatus
 MetroWidget::ApzReceiveInputEvent(WidgetInputEvent* aEvent,
-                                  ScrollableLayerGuid* aOutTargetGuid)
+                                  ScrollableLayerGuid* aOutTargetGuid,
+                                  uint64_t* aOutInputBlockId)
 {
   MOZ_ASSERT(aEvent);
 
   if (!mController) {
     return nsEventStatus_eIgnore;
   }
-  return mController->ReceiveInputEvent(aEvent, aOutTargetGuid);
+  return mController->ReceiveInputEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
 }
 
 void
 MetroWidget::SetApzPendingResponseFlusher(APZPendingResponseFlusher* aFlusher)
 {
   mController->SetPendingResponseFlusher(aFlusher);
 }
 
--- a/widget/windows/winrt/MetroWidget.h
+++ b/widget/windows/winrt/MetroWidget.h
@@ -204,30 +204,31 @@ public:
   void FindMetroWindow();
   virtual void SetTransparencyMode(nsTransparencyMode aMode);
   virtual nsTransparencyMode GetTransparencyMode();
 
   TouchBehaviorFlags ContentGetAllowedTouchBehavior(const nsIntPoint& aPoint);
 
   // apzc controller related api
   void ApzcGetAllowedTouchBehavior(mozilla::WidgetInputEvent* aTransformedEvent, nsTArray<TouchBehaviorFlags>& aOutBehaviors);
-  void ApzcSetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid, nsTArray<TouchBehaviorFlags>& aBehaviors);
+  void ApzcSetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId, nsTArray<TouchBehaviorFlags>& aBehaviors);
 
   // Hit test a point to see if an apzc would consume input there
   bool ApzHitTest(mozilla::ScreenIntPoint& pt);
   // Transforms a coord so that it properly targets gecko content based
   // on apzc transforms currently applied.
   void ApzTransformGeckoCoordinate(const mozilla::ScreenIntPoint& pt,
                                    mozilla::LayoutDeviceIntPoint* aRefPointOut);
   // send ContentRecievedTouch calls to the apz with appropriate preventDefault params
-  void ApzContentConsumingTouch(const ScrollableLayerGuid& aGuid);
-  void ApzContentIgnoringTouch(const ScrollableLayerGuid& aGuid);
+  void ApzContentConsumingTouch(const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId);
+  void ApzContentIgnoringTouch(const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId);
   // Input handling
   nsEventStatus ApzReceiveInputEvent(mozilla::WidgetInputEvent* aEvent,
-                                     ScrollableLayerGuid* aOutTargetGuid);
+                                     ScrollableLayerGuid* aOutTargetGuid,
+                                     uint64_t* aOutInputBlockId);
   // Callback for the APZController
   void SetApzPendingResponseFlusher(APZPendingResponseFlusher* aFlusher);
 
 protected:
   friend class FrameworkView;
 
   struct OleInitializeWrapper {
     HRESULT const hr;