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 id27704
push userkwierso@gmail.com
push dateSat, 25 Oct 2014 01:25:30 +0000
treeherdermozilla-central@e37231060eb4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1083395
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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;