Bug 1256339 - Collapse the different Handle*Tap functions in GeckoContentController into a single API. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 05 Jul 2016 13:24:54 -0400
changeset 303713 b8f47976eb5aa814e28278ce5ab71d75b2ff5b57
parent 303712 3f7577ec0b2c407e6be478ee21a727b06cad468e
child 303714 fefd3e69b3695aa3f1439407b4f8b2d91e6aafe8
push id79148
push userkgupta@mozilla.com
push dateTue, 05 Jul 2016 17:25:15 +0000
treeherdermozilla-inbound@6172a55a6ae8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1256339
milestone50.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 1256339 - Collapse the different Handle*Tap functions in GeckoContentController into a single API. r=botond This is just a refactoring, no functional changes intended. MozReview-Commit-ID: GRJxVpNAlHC
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
gfx/ipc/GfxMessageUtils.h
gfx/layers/apz/public/GeckoContentController.h
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/test/gtest/APZTestCommon.h
gfx/layers/apz/test/gtest/TestEventRegions.cpp
gfx/layers/apz/test/gtest/TestGestureDetector.cpp
gfx/layers/apz/test/gtest/TestHitTesting.cpp
gfx/layers/apz/test/gtest/TestTreeManager.cpp
gfx/layers/apz/util/ChromeProcessController.cpp
gfx/layers/apz/util/ChromeProcessController.h
gfx/layers/ipc/APZChild.cpp
gfx/layers/ipc/APZChild.h
gfx/layers/ipc/PAPZ.ipdl
gfx/layers/ipc/RemoteContentController.cpp
gfx/layers/ipc/RemoteContentController.h
widget/android/AndroidContentController.cpp
widget/android/AndroidContentController.h
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -133,16 +133,17 @@ using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::dom::workers;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::docshell;
 using namespace mozilla::widget;
 using namespace mozilla::jsipc;
+using mozilla::layers::GeckoContentController;
 
 NS_IMPL_ISUPPORTS(ContentListener, nsIDOMEventListener)
 
 static const CSSSize kDefaultViewportSize(980, 480);
 
 static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
 
 typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
@@ -1779,37 +1780,44 @@ TabChild::HandleDoubleTap(const CSSPoint
       document->GetDocumentElement(), &presShellId, &viewId) &&
       mAPZChild) {
     mAPZChild->SendZoomToRect(presShellId, viewId, zoomToRect,
                               DEFAULT_BEHAVIOR);
   }
 }
 
 void
-TabChild::HandleSingleTap(const CSSPoint& aPoint,
-                          const Modifiers& aModifiers,
-                          const ScrollableLayerGuid& aGuid,
-                          bool aCallTakeFocusForClickFromTap)
+TabChild::HandleTap(GeckoContentController::TapType aType,
+                    const CSSPoint& aPoint, const Modifiers& aModifiers,
+                    const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId,
+                    bool aCallTakeFocusForClickFromTap)
 {
-  if (aCallTakeFocusForClickFromTap && mRemoteFrame) {
-    mRemoteFrame->SendTakeFocusForClickFromTap();
-  }
-  if (mGlobal && mTabChildGlobal) {
-    mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid);
-  }
-}
-
-void
-TabChild::HandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
-                        const ScrollableLayerGuid& aGuid,
-                        const uint64_t& aInputBlockId)
-{
-  if (mGlobal && mTabChildGlobal) {
-    mAPZEventState->ProcessLongTap(GetPresShell(), aPoint, aModifiers, aGuid,
-        aInputBlockId);
+  switch (aType) {
+  case GeckoContentController::TapType::eSingleTap:
+    if (aCallTakeFocusForClickFromTap && mRemoteFrame) {
+      mRemoteFrame->SendTakeFocusForClickFromTap();
+    }
+    if (mGlobal && mTabChildGlobal) {
+      mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid);
+    }
+    break;
+  case GeckoContentController::TapType::eDoubleTap:
+    HandleDoubleTap(aPoint, aModifiers, aGuid);
+    break;
+  case GeckoContentController::TapType::eLongTap:
+    if (mGlobal && mTabChildGlobal) {
+      mAPZEventState->ProcessLongTap(GetPresShell(), aPoint, aModifiers, aGuid,
+          aInputBlockId);
+    }
+    break;
+  case GeckoContentController::TapType::eSentinel:
+    // Should never happen, but we need to handle this case to make the compiler
+    // happy.
+    MOZ_ASSERT(false);
+    break;
   }
 }
 
 bool
 TabChild::NotifyAPZStateChange(const ViewID& aViewId,
                                const layers::GeckoContentController::APZStateChange& aChange,
                                const int& aArg)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -619,27 +619,22 @@ public:
                   PRenderFrameChild* aRenderFrame,
                   const ShowInfo& aShowInfo);
 
   void ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
                                  uint64_t aInputBlockId,
                                  bool aPreventDefault) const;
   void SetTargetAPZC(uint64_t aInputBlockId,
                     const nsTArray<ScrollableLayerGuid>& aTargets) const;
-  void HandleDoubleTap(const CSSPoint& aPoint,
-                       const Modifiers& aModifiers,
-                       const mozilla::layers::ScrollableLayerGuid& aGuid);
-  void HandleSingleTap(const CSSPoint& aPoint,
-                       const Modifiers& aModifiers,
-                       const mozilla::layers::ScrollableLayerGuid& aGuid,
-                       bool aCallTakeFocusForClickFromTap);
-  void HandleLongTap(const CSSPoint& aPoint,
-                     const Modifiers& aModifiers,
-                     const mozilla::layers::ScrollableLayerGuid& aGuid,
-                     const uint64_t& aInputBlockId);
+  void HandleTap(layers::GeckoContentController::TapType aType,
+                 const CSSPoint& aPoint,
+                 const Modifiers& aModifiers,
+                 const mozilla::layers::ScrollableLayerGuid& aGuid,
+                 const uint64_t& aInputBlockId,
+                 bool aCallTakeFocusForClickFromTap);
   void SetAllowedTouchBehavior(uint64_t aInputBlockId,
                                const nsTArray<TouchBehaviorFlags>& aFlags) const;
 
   bool UpdateFrame(const FrameMetrics& aFrameMetrics);
   bool NotifyAPZStateChange(const ViewID& aViewId,
                             const layers::GeckoContentController::APZStateChange& aChange,
                             const int& aArg);
   void StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics);
@@ -684,16 +679,19 @@ protected:
   virtual bool RecvMenuKeyboardListenerInstalled(
                  const bool& aInstalled) override;
 
 #ifdef MOZ_WIDGET_GONK
   void MaybeRequestPreinitCamera();
 #endif
 
 private:
+  void HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
+                       const ScrollableLayerGuid& aGuid);
+
   // Notify others that our TabContext has been updated.  (At the moment, this
   // sets the appropriate origin attributes on our docshell.)
   //
   // You should call this after calling TabContext::SetTabContext().  We also
   // call this during Init().
   void NotifyTabContextUpdated();
 
   // Update the frameType on our docshell.
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -1165,16 +1165,26 @@ struct ParamTraits<mozilla::gfx::FilterD
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     return (ReadParam(aMsg, aIter, &aResult->mPrimitives));
   }
 };
 
+typedef mozilla::layers::GeckoContentController::TapType TapType;
+
+template <>
+struct ParamTraits<TapType>
+  : public ContiguousEnumSerializer<
+             TapType,
+             TapType::eSingleTap,
+             TapType::eSentinel>
+{};
+
 typedef mozilla::layers::GeckoContentController::APZStateChange APZStateChange;
 
 template <>
 struct ParamTraits<APZStateChange>
   : public ContiguousEnumSerializer<
              APZStateChange,
              APZStateChange::eTransformBegin,
              APZStateChange::eSentinel>
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -33,42 +33,36 @@ public:
   /**
    * Requests a paint of the given FrameMetrics |aFrameMetrics| from Gecko.
    * Implementations per-platform are responsible for actually handling this.
    * This method will always be called on the Gecko main thread.
    */
   virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) = 0;
 
   /**
-   * Requests handling of a double tap. |aPoint| is in CSS pixels, relative to
-   * the current scroll offset. This should eventually round-trip back to
-   * AsyncPanZoomController::ZoomToRect with the dimensions that we want to zoom
-   * to.
+   * Different types of tap-related events that can be sent in
+   * the HandleTap function. The names should be relatively self-explanatory.
    */
-  virtual void HandleDoubleTap(const CSSPoint& aPoint,
-                               Modifiers aModifiers,
-                               const ScrollableLayerGuid& aGuid) = 0;
+  enum class TapType {
+    eSingleTap,
+    eDoubleTap,
+    eLongTap,
+
+    eSentinel,
+  };
 
   /**
-   * Requests handling a single tap. |aPoint| is in CSS pixels, relative to the
-   * current scroll offset. This should simulate and send to content a mouse
-   * button down, then mouse button up at |aPoint|.
-   */
-  virtual void HandleSingleTap(const CSSPoint& aPoint,
-                               Modifiers aModifiers,
-                               const ScrollableLayerGuid& aGuid) = 0;
-
-  /**
-   * Requests handling a long tap. |aPoint| is in CSS pixels, relative to the
+   * Requests handling of a tap event. |aPoint| is in CSS pixels, relative to the
    * current scroll offset.
    */
-  virtual void HandleLongTap(const CSSPoint& aPoint,
-                             Modifiers aModifiers,
-                             const ScrollableLayerGuid& aGuid,
-                             uint64_t aInputBlockId) = 0;
+  virtual void HandleTap(TapType aType,
+                         const CSSPoint& aPoint,
+                         Modifiers aModifiers,
+                         const ScrollableLayerGuid& aGuid,
+                         uint64_t aInputBlockId) = 0;
 
   /**
    * Schedules a runnable to run on the controller/UI thread at some time
    * in the future.
    * This method must always be called on the controller thread.
    */
   virtual void PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs) = 0;
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -94,16 +94,17 @@
 #  define APZC_LOG_FM(fm, prefix, ...)
 #endif
 
 namespace mozilla {
 namespace layers {
 
 typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
 typedef GeckoContentController::APZStateChange APZStateChange;
+typedef GeckoContentController::TapType TapType;
 typedef mozilla::gfx::Point Point;
 typedef mozilla::gfx::Matrix4x4 Matrix4x4;
 using mozilla::gfx::PointTyped;
 
 // Choose between platform-specific implementations.
 #ifdef MOZ_ANDROID_APZ
 typedef WidgetOverscrollEffect OverscrollEffect;
 typedef AndroidSpecificState PlatformSpecificState;
@@ -1990,17 +1991,17 @@ nsEventStatus AsyncPanZoomController::On
         APZC_LOG("%p dropping long-press because some non-touch block interrupted it\n", this);
         return nsEventStatus_eIgnore;
       }
       if (block->AsTouchBlock()->IsDuringFastFling()) {
         APZC_LOG("%p dropping long-press because of fast fling\n", this);
         return nsEventStatus_eIgnore;
       }
       uint64_t blockId = GetInputQueue()->InjectNewTouchBlock(this);
-      controller->HandleLongTap(geckoScreenPoint, aEvent.modifiers, GetGuid(), blockId);
+      controller->HandleTap(TapType::eLongTap, geckoScreenPoint, aEvent.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);
@@ -2024,26 +2025,27 @@ nsEventStatus AsyncPanZoomController::Ge
       if (touch) {
         if (touch->IsDuringFastFling()) {
           APZC_LOG("%p dropping single-tap because it was during a fast-fling\n", this);
           return nsEventStatus_eIgnore;
         }
         touch->SetSingleTapOccurred();
       }
       // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
-      // calling controller->HandleSingleTap directly might mean that content receives
+      // calling controller->HandleTap directly might mean that content receives
       // the single tap message before the corresponding touch-up. To avoid that we
       // schedule the singletap message to run on the next spin of the event loop.
       // See bug 965381 for the issue this was causing.
       RefPtr<Runnable> runnable =
-        NewRunnableMethod<CSSPoint,
-                          mozilla::Modifiers,
-                          ScrollableLayerGuid>(controller, &GeckoContentController::HandleSingleTap,
-                                               geckoScreenPoint, aModifiers,
-                                               GetGuid());
+        NewRunnableMethod<TapType, CSSPoint, mozilla::Modifiers,
+                          ScrollableLayerGuid, uint64_t>(controller,
+                            &GeckoContentController::HandleTap,
+                            TapType::eSingleTap, geckoScreenPoint,
+                            aModifiers, GetGuid(),
+                            touch ? touch->GetBlockId() : 0);
 
       controller->PostDelayedTask(runnable.forget(), 0);
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
@@ -2071,17 +2073,18 @@ nsEventStatus AsyncPanZoomController::On
 
 nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a double-tap in state %d\n", this, mState);
   RefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     if (mZoomConstraints.mAllowDoubleTapZoom && CurrentTouchBlock()->TouchActionAllowsDoubleTapZoom()) {
       CSSPoint geckoScreenPoint;
       if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
-        controller->HandleDoubleTap(geckoScreenPoint, aEvent.modifiers, GetGuid());
+        controller->HandleTap(TapType::eDoubleTap, geckoScreenPoint,
+            aEvent.modifiers, GetGuid(), CurrentTouchBlock()->GetBlockId());
       }
     }
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
--- a/gfx/layers/apz/test/gtest/APZTestCommon.h
+++ b/gfx/layers/apz/test/gtest/APZTestCommon.h
@@ -35,16 +35,17 @@ using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using ::testing::_;
 using ::testing::NiceMock;
 using ::testing::AtLeast;
 using ::testing::AtMost;
 using ::testing::MockFunction;
 using ::testing::InSequence;
+typedef mozilla::layers::GeckoContentController::TapType TapType;
 
 template<class T>
 class ScopedGfxPref {
 public:
   ScopedGfxPref(T (*aGetPrefFunc)(void), void (*aSetPrefFunc)(T), T aVal)
     : mSetPrefFunc(aSetPrefFunc)
   {
     mOldVal = aGetPrefFunc();
@@ -71,19 +72,17 @@ static TimeStamp GetStartupTime() {
   return sStartupTime;
 }
 
 class MockContentController : public GeckoContentController {
 public:
   MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
   MOCK_METHOD2(RequestFlingSnap, void(const FrameMetrics::ViewID& aScrollId, const mozilla::CSSPoint& aDestination));
   MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
-  MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&));
-  MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&));
-  MOCK_METHOD4(HandleLongTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&, uint64_t));
+  MOCK_METHOD5(HandleTap, void(TapType, const CSSPoint&, Modifiers, const ScrollableLayerGuid&, uint64_t));
   // Can't use the macros with already_AddRefed :(
   void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
     RefPtr<Runnable> task = aTask;
   }
   MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
   MOCK_METHOD0(NotifyFlushComplete, void());
 };
 
--- a/gfx/layers/apz/test/gtest/TestEventRegions.cpp
+++ b/gfx/layers/apz/test/gtest/TestEventRegions.cpp
@@ -165,26 +165,26 @@ TEST_F(APZEventRegionsTester, HitRegionI
 
   TestAsyncPanZoomController* root = ApzcOf(layers[0]);
   TestAsyncPanZoomController* left = ApzcOf(layers[1]);
   TestAsyncPanZoomController* bottom = ApzcOf(layers[2]);
 
   MockFunction<void(std::string checkPointName)> check;
   {
     InSequence s;
-    EXPECT_CALL(*mcc, HandleSingleTap(_, _, left->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped on left"));
-    EXPECT_CALL(*mcc, HandleSingleTap(_, _, bottom->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped on bottom"));
-    EXPECT_CALL(*mcc, HandleSingleTap(_, _, root->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, root->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped on root"));
     EXPECT_CALL(check, Call("Tap pending on d-t-c region"));
-    EXPECT_CALL(*mcc, HandleSingleTap(_, _, bottom->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped on bottom again"));
-    EXPECT_CALL(*mcc, HandleSingleTap(_, _, left->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped on left this time"));
   }
 
   TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
 
   // Tap in the exposed hit regions of each of the layers once and ensure
   // the clicks are dispatched right away
   Tap(manager, ScreenIntPoint(10, 10), tapDuration);
@@ -216,17 +216,17 @@ TEST_F(APZEventRegionsTester, HitRegionI
 
 TEST_F(APZEventRegionsTester, HitRegionAccumulatesChildren) {
   CreateEventRegionsLayerTree2();
 
   // Tap in the area of the child layer that's not directly included in the
   // parent layer's hit region. Verify that it comes out of the APZC's
   // content controller, which indicates the input events got routed correctly
   // to the APZC.
-  EXPECT_CALL(*mcc, HandleSingleTap(_, _, rootApzc->GetGuid())).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, rootApzc->GetGuid(), _)).Times(1);
   Tap(manager, ScreenIntPoint(10, 160), TimeDuration::FromMilliseconds(100));
 }
 
 TEST_F(APZEventRegionsTester, Obscuration) {
   CreateObscuringLayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
 
   manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
@@ -259,14 +259,14 @@ TEST_F(APZEventRegionsTester, Bug1117712
   TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]);
 
   // These touch events should hit the dispatch-to-content region of layers[3]
   // and so get queued with that APZC as the tentative target.
   uint64_t inputBlockId = 0;
   Tap(manager, ScreenIntPoint(55, 5), TimeDuration::FromMilliseconds(100), nullptr, &inputBlockId);
   // But now we tell the APZ that really it hit layers[2], and expect the tap
   // to be delivered at the correct coordinates.
-  EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(55, 5), 0, apzc2->GetGuid())).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(55, 5), 0, apzc2->GetGuid(), _)).Times(1);
 
   nsTArray<ScrollableLayerGuid> targets;
   targets.AppendElement(apzc2->GetGuid());
   manager->SetTargetAPZC(inputBlockId, targets);
 }
--- a/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
+++ b/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
@@ -202,19 +202,19 @@ protected:
     int timeDelta = aSlow ? 2000 : 10;
     int tapCallsExpected = aSlow ? 2 : 1;
 
     // Advance the fling animation by timeDelta milliseconds.
     ParentLayerPoint pointOut;
     AsyncTransform viewTransformOut;
     apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(timeDelta));
 
-    // Deliver a tap to abort the fling. Ensure that we get a HandleSingleTap
+    // Deliver a tap to abort the fling. Ensure that we get a SingleTap
     // call out of it if and only if the fling is slow.
-    EXPECT_CALL(*mcc, HandleSingleTap(_, 0, apzc->GetGuid())).Times(tapCallsExpected);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _)).Times(tapCallsExpected);
     Tap(apzc, ScreenIntPoint(10, 10), 0);
     while (mcc->RunThroughDelayedTasks());
 
     // Deliver another tap, to make sure that taps are flowing properly once
     // the fling is aborted.
     Tap(apzc, ScreenIntPoint(100, 100), 0);
     while (mcc->RunThroughDelayedTasks());
 
@@ -295,17 +295,17 @@ TEST_F(APZCGestureDetectorTester, ShortP
 
   MockFunction<void(std::string checkPointName)> check;
   {
     InSequence s;
     // This verifies that the single tap notification is sent after the
     // touchup is fully processed. The ordering here is important.
     EXPECT_CALL(check, Call("pre-tap"));
     EXPECT_CALL(check, Call("post-tap"));
-    EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
   }
 
   check.Call("pre-tap");
   TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
   check.Call("post-tap");
 
   apzc->AssertStateIsReset();
 }
@@ -315,17 +315,17 @@ TEST_F(APZCGestureDetectorTester, Medium
 
   MockFunction<void(std::string checkPointName)> check;
   {
     InSequence s;
     // This verifies that the single tap notification is sent after the
     // touchup is fully processed. The ordering here is important.
     EXPECT_CALL(check, Call("pre-tap"));
     EXPECT_CALL(check, Call("post-tap"));
-    EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
   }
 
   check.Call("pre-tap");
   TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(400));
   check.Call("post-tap");
 
   apzc->AssertStateIsReset();
 }
@@ -351,21 +351,21 @@ protected:
 
     MockFunction<void(std::string checkPointName)> check;
 
     {
       InSequence s;
 
       EXPECT_CALL(check, Call("preHandleLongTap"));
       blockId++;
-      EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1);
+      EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, CSSPoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1);
       EXPECT_CALL(check, Call("postHandleLongTap"));
 
       EXPECT_CALL(check, Call("preHandleSingleTap"));
-      EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+      EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
       EXPECT_CALL(check, Call("postHandleSingleTap"));
     }
 
     // Manually invoke the longpress while the touch is currently down.
     check.Call("preHandleLongTap");
     mcc->RunThroughDelayedTasks();
     check.Call("postHandleLongTap");
 
@@ -412,17 +412,17 @@ protected:
 
     MockFunction<void(std::string checkPointName)> check;
 
     {
       InSequence s;
 
       EXPECT_CALL(check, Call("preHandleLongTap"));
       blockId++;
-      EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(touchX, touchStartY), 0, apzc->GetGuid(), blockId)).Times(1);
+      EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, CSSPoint(touchX, touchStartY), 0, apzc->GetGuid(), blockId)).Times(1);
       EXPECT_CALL(check, Call("postHandleLongTap"));
     }
 
     // Manually invoke the longpress while the touch is currently down.
     check.Call("preHandleLongTap");
     mcc->RunThroughDelayedTasks();
     check.Call("postHandleLongTap");
 
@@ -434,17 +434,17 @@ protected:
     apzc->ContentReceivedInputBlock(blockId, true);
     mcc->AdvanceByMillis(1000);
 
     MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time());
     mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
     status = apzc->ReceiveInputEvent(mti, nullptr);
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
-    EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(0);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(touchX, touchEndY), 0, apzc->GetGuid(), _)).Times(0);
     status = TouchUp(apzc, ScreenIntPoint(touchX, touchEndY), mcc->Time());
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
     ParentLayerPoint pointOut;
     AsyncTransform viewTransformOut;
     apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
 
     EXPECT_EQ(ParentLayerPoint(), pointOut);
@@ -477,86 +477,86 @@ TEST_F(APZCLongPressTester, LongPressPre
                                 | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
                                 | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTap) {
   MakeApzcWaitForMainThread();
   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);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
 
   uint64_t blockIds[2];
   DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], false);
   apzc->ContentReceivedInputBlock(blockIds[1], false);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
   MakeApzcWaitForMainThread();
   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);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(2);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
 
   uint64_t blockIds[2];
   DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], false);
   apzc->ContentReceivedInputBlock(blockIds[1], false);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) {
   MakeApzcWaitForMainThread();
   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);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
 
   uint64_t blockIds[2];
   DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], true);
   apzc->ContentReceivedInputBlock(blockIds[1], false);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
   MakeApzcWaitForMainThread();
   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);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
 
   uint64_t blockIds[2];
   DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], true);
   apzc->ContentReceivedInputBlock(blockIds[1], true);
 
   apzc->AssertStateIsReset();
 }
 
 // Test for bug 947892
 // We test whether we dispatch tap event when the tap is followed by pinch.
 TEST_F(APZCGestureDetectorTester, TapFollowedByPinch) {
   MakeApzcZoomable();
 
-  EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
 
   Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
 
   int inputId = 0;
   MultiTouchInput mti;
   mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
   mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
   mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
@@ -568,17 +568,17 @@ TEST_F(APZCGestureDetectorTester, TapFol
   apzc->ReceiveInputEvent(mti, nullptr);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) {
   MakeApzcZoomable();
 
-  EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
 
   Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
 
   int inputId = 0;
   MultiTouchInput mti;
   mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
   mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
   apzc->ReceiveInputEvent(mti, nullptr);
@@ -595,32 +595,32 @@ TEST_F(APZCGestureDetectorTester, TapFol
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, LongPressInterruptedByWheel) {
   // Since the wheel block interrupted the long-press, we don't expect
   // any long-press notifications. However, this also shouldn't crash, which
   // is what it used to do.
-  EXPECT_CALL(*mcc, HandleLongTap(_, _, _, _)).Times(0);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(0);
 
   uint64_t touchBlockId = 0;
   uint64_t wheelBlockId = 0;
   TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &touchBlockId);
   mcc->AdvanceByMillis(10);
   Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time(), &wheelBlockId);
   EXPECT_NE(touchBlockId, wheelBlockId);
   mcc->AdvanceByMillis(1000);
 }
 
 TEST_F(APZCGestureDetectorTester, TapTimeoutInterruptedByWheel) {
   // In this test, even though the wheel block comes right after the tap, the
   // tap should still be dispatched because it completes fully before the wheel
   // block arrived.
-  EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
 
   // We make the APZC zoomable so the gesture detector needs to wait to
   // distinguish between tap and double-tap. During that timeout is when we
   // insert the wheel event.
   MakeApzcZoomable();
 
   uint64_t touchBlockId = 0;
   uint64_t wheelBlockId = 0;
--- a/gfx/layers/apz/test/gtest/TestHitTesting.cpp
+++ b/gfx/layers/apz/test/gtest/TestHitTesting.cpp
@@ -504,19 +504,19 @@ TEST_F(APZHitTestingTester, TestForceDis
 TEST_F(APZHitTestingTester, Bug1148350) {
   CreateBug1148350LayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
   manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
 
   MockFunction<void(std::string checkPointName)> check;
   {
     InSequence s;
-    EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(100, 100), 0, ApzcOf(layers[1])->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped without transform"));
-    EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(100, 100), 0, ApzcOf(layers[1])->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped with interleaved transform"));
   }
 
   Tap(manager, ScreenIntPoint(100, 100), TimeDuration::FromMilliseconds(100));
   mcc->RunThroughDelayedTasks();
   check.Call("Tapped without transform");
 
   uint64_t blockId;
--- a/gfx/layers/apz/test/gtest/TestTreeManager.cpp
+++ b/gfx/layers/apz/test/gtest/TestTreeManager.cpp
@@ -86,17 +86,17 @@ TEST_F(APZCTreeManagerTester, Bug1194876
   manager->ContentReceivedInputBlock(blockId, false);
   targets.AppendElement(ApzcOf(layers[0])->GetGuid());
   manager->SetTargetAPZC(blockId, targets);
 
   // Around here, the above multi-touch will get processed by ApzcOf(layers[1]).
   // We want to ensure that ApzcOf(layers[0]) has had its state cleared, because
   // otherwise it will do things like dispatch spurious long-tap events.
 
-  EXPECT_CALL(*mcc, HandleLongTap(_, _, _, _)).Times(0);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(0);
 }
 
 TEST_F(APZCTreeManagerTester, Bug1198900) {
   // This is just a test that cancels a wheel event to make sure it doesn't
   // crash.
   CreateSimpleDTCScrollingLayer();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
   manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -113,25 +113,17 @@ ChromeProcessController::GetRootContentD
   return nullptr;
 }
 
 void
 ChromeProcessController::HandleDoubleTap(const mozilla::CSSPoint& aPoint,
                                          Modifiers aModifiers,
                                          const ScrollableLayerGuid& aGuid)
 {
-  if (MessageLoop::current() != mUILoop) {
-    mUILoop->PostTask(NewRunnableMethod
-                      <CSSPoint,
-                       Modifiers,
-                       ScrollableLayerGuid>(this,
-                                            &ChromeProcessController::HandleDoubleTap,
-                                            aPoint, aModifiers, aGuid));
-    return;
-  }
+  MOZ_ASSERT(MessageLoop::current() == mUILoop);
 
   nsCOMPtr<nsIDocument> document = GetRootContentDocument(aGuid.mScrollId);
   if (!document.get()) {
     return;
   }
 
   CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
   // CalculateRectToZoomTo performs a hit test on the frame associated with the
@@ -149,50 +141,46 @@ ChromeProcessController::HandleDoubleTap
   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
       document->GetDocumentElement(), &presShellId, &viewId)) {
     mAPZCTreeManager->ZoomToRect(
       ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId), zoomToRect);
   }
 }
 
 void
-ChromeProcessController::HandleSingleTap(const CSSPoint& aPoint,
-                                         Modifiers aModifiers,
-                                         const ScrollableLayerGuid& aGuid)
+ChromeProcessController::HandleTap(TapType aType,
+                                   const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
+                                   const ScrollableLayerGuid& aGuid,
+                                   uint64_t aInputBlockId)
 {
   if (MessageLoop::current() != mUILoop) {
-    mUILoop->PostTask(NewRunnableMethod
-                      <CSSPoint,
-                       Modifiers,
-                      ScrollableLayerGuid>(this,
-                                           &ChromeProcessController::HandleSingleTap,
-                                           aPoint, aModifiers, aGuid));
+    mUILoop->PostTask(NewRunnableMethod<TapType, mozilla::CSSPoint, Modifiers,
+                                        ScrollableLayerGuid, uint64_t>(this,
+                        &ChromeProcessController::HandleTap,
+                        aType, aPoint, aModifiers, aGuid, aInputBlockId));
     return;
   }
 
-  mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid);
-}
-
-void
-ChromeProcessController::HandleLongTap(const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
-                                       const ScrollableLayerGuid& aGuid,
-                                       uint64_t aInputBlockId)
-{
-  if (MessageLoop::current() != mUILoop) {
-    mUILoop->PostTask(NewRunnableMethod
-                      <mozilla::CSSPoint,
-                       Modifiers,
-                       ScrollableLayerGuid,
-                       uint64_t>(this, &ChromeProcessController::HandleLongTap,
-                                 aPoint, aModifiers, aGuid, aInputBlockId));
-    return;
+  switch (aType) {
+  case TapType::eSingleTap:
+    mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid);
+    break;
+  case TapType::eDoubleTap:
+    HandleDoubleTap(aPoint, aModifiers, aGuid);
+    break;
+  case TapType::eLongTap:
+    mAPZEventState->ProcessLongTap(GetPresShell(), aPoint, aModifiers, aGuid,
+        aInputBlockId);
+    break;
+  case TapType::eSentinel:
+    // Should never happen, but we need to handle this case branch for the
+    // compiler to be happy.
+    MOZ_ASSERT(false);
+    break;
   }
-
-  mAPZEventState->ProcessLongTap(GetPresShell(), aPoint, aModifiers, aGuid,
-      aInputBlockId);
 }
 
 void
 ChromeProcessController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                               APZStateChange aChange,
                                               int aArg)
 {
   if (MessageLoop::current() != mUILoop) {
--- a/gfx/layers/apz/util/ChromeProcessController.h
+++ b/gfx/layers/apz/util/ChromeProcessController.h
@@ -35,37 +35,36 @@ protected:
 public:
   explicit ChromeProcessController(nsIWidget* aWidget, APZEventState* aAPZEventState, APZCTreeManager* aAPZCTreeManager);
   ~ChromeProcessController();
   virtual void Destroy() override;
 
   // GeckoContentController interface
   virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override;
   virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
-  virtual void HandleDoubleTap(const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
-                               const ScrollableLayerGuid& aGuid) override;
-  virtual void HandleSingleTap(const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
-                               const ScrollableLayerGuid& aGuid) override;
-  virtual void HandleLongTap(const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
-                               const ScrollableLayerGuid& aGuid,
-                               uint64_t aInputBlockId) override;
+  virtual void HandleTap(TapType aType,
+                         const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
+                         const ScrollableLayerGuid& aGuid,
+                         uint64_t aInputBlockId) override;
   virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                     APZStateChange aChange,
                                     int aArg) override;
   virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
                                          const nsString& aEvent) override;
   virtual void NotifyFlushComplete() override;
 private:
   nsCOMPtr<nsIWidget> mWidget;
   RefPtr<APZEventState> mAPZEventState;
   RefPtr<APZCTreeManager> mAPZCTreeManager;
   MessageLoop* mUILoop;
 
   void InitializeRoot();
   nsIPresShell* GetPresShell() const;
   nsIDocument* GetRootDocument() const;
   nsIDocument* GetRootContentDocument(const FrameMetrics::ViewID& aScrollId) const;
+  void HandleDoubleTap(const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
+                       const ScrollableLayerGuid& aGuid);
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* mozilla_layers_ChromeProcessController_h */
--- a/gfx/layers/ipc/APZChild.cpp
+++ b/gfx/layers/ipc/APZChild.cpp
@@ -95,42 +95,25 @@ APZChild::~APZChild()
 
 bool
 APZChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
 {
   return mBrowser->UpdateFrame(aFrameMetrics);
 }
 
 bool
-APZChild::RecvHandleDoubleTap(const CSSPoint& aPoint,
-                              const Modifiers& aModifiers,
-                              const ScrollableLayerGuid& aGuid)
+APZChild::RecvHandleTap(const TapType& aType,
+                        const CSSPoint& aPoint,
+                        const Modifiers& aModifiers,
+                        const ScrollableLayerGuid& aGuid,
+                        const uint64_t& aInputBlockId,
+                        const bool& aCallTakeFocusForClickFromTap)
 {
-  mBrowser->HandleDoubleTap(aPoint, aModifiers, aGuid);
-  return true;
-}
-
-bool
-APZChild::RecvHandleSingleTap(const CSSPoint& aPoint,
-                              const Modifiers& aModifiers,
-                              const ScrollableLayerGuid& aGuid,
-                              const bool& aCallTakeFocusForClickFromTap)
-{
-  mBrowser->HandleSingleTap(aPoint, aModifiers, aGuid,
-                            aCallTakeFocusForClickFromTap);
-  return true;
-}
-
-bool
-APZChild::RecvHandleLongTap(const CSSPoint& aPoint,
-                            const Modifiers& aModifiers,
-                            const ScrollableLayerGuid& aGuid,
-                            const uint64_t& aInputBlockId)
-{
-  mBrowser->HandleLongTap(aPoint, aModifiers, aGuid, aInputBlockId);
+  mBrowser->HandleTap(aType, aPoint, aModifiers, aGuid,
+      aInputBlockId, aCallTakeFocusForClickFromTap);
   return true;
 }
 
 bool
 APZChild::RecvNotifyAPZStateChange(const ViewID& aViewId,
                                    const APZStateChange& aChange,
                                    const int& aArg)
 {
--- a/gfx/layers/ipc/APZChild.h
+++ b/gfx/layers/ipc/APZChild.h
@@ -23,29 +23,22 @@ class APZChild final : public PAPZChild
 {
 public:
   static APZChild* Create(const dom::TabId& aTabId);
 
   ~APZChild();
 
   virtual bool RecvUpdateFrame(const FrameMetrics& frame) override;
 
-  virtual bool RecvHandleDoubleTap(const CSSPoint& aPoint,
-                                   const Modifiers& aModifiers,
-                                   const ScrollableLayerGuid& aGuid) override;
-
-  virtual bool RecvHandleSingleTap(const CSSPoint& aPoint,
-                                   const Modifiers& aModifiers,
-                                   const ScrollableLayerGuid& aGuid,
-                                   const bool& aCallTakeFocusForClickFromTap) override;
-
-  virtual bool RecvHandleLongTap(const CSSPoint& aPoint,
-                                 const Modifiers& aModifiers,
-                                 const ScrollableLayerGuid& aGuid,
-                                 const uint64_t& aInputBlockId) override;
+  virtual bool RecvHandleTap(const TapType& aType,
+                             const CSSPoint& aPoint,
+                             const Modifiers& aModifiers,
+                             const ScrollableLayerGuid& aGuid,
+                             const uint64_t& aInputBlockId,
+                             const bool& aCallTakeFocusForClickFromTap) override;
 
   virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId,
                                         const APZStateChange& aChange,
                                         const int& aArg) override;
 
   virtual bool RecvNotifyFlushComplete() override;
 
   virtual bool RecvDestroy() override;
--- a/gfx/layers/ipc/PAPZ.ipdl
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -11,16 +11,17 @@ include protocol PContent;
 
 using mozilla::CSSPoint from "Units.h";
 using CSSRect from "Units.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
 using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
+using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/GeckoContentController.h";
 using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h";
 using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
 using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
 using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
 using mozilla::Modifiers from "mozilla/EventForwards.h";
 using class nsRegion from "nsRegion.h";
 
 namespace mozilla {
@@ -86,19 +87,22 @@ parent:
   async __delete__();
 
 child:
   async 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.
-  async HandleDoubleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
-  async HandleSingleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid, bool aCallTakeFocusForClickFromTap);
-  async HandleLongTap(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+  // The aCallTakeFocusForClickFromTap argument is used for eSingleTap types,
+  // to request that the child take focus before dispatching the mouse events
+  // for the tap (otherwise the resulting focus behaviour is incorrect).
+  async HandleTap(TapType aType, CSSPoint point, Modifiers aModifiers,
+                  ScrollableLayerGuid aGuid, uint64_t aInputBlockId,
+                  bool aCallTakeFocusForClickFromTap);
   async NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
   async NotifyFlushComplete();
 
   async Destroy();
 };
 
 } // layers
 } // mozilla
--- a/gfx/layers/ipc/RemoteContentController.cpp
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -48,90 +48,47 @@ RemoteContentController::RequestContentR
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (CanSend()) {
     Unused << SendUpdateFrame(aFrameMetrics);
   }
 }
 
 void
-RemoteContentController::HandleDoubleTap(const CSSPoint& aPoint,
-                                         Modifiers aModifiers,
-                                         const ScrollableLayerGuid& aGuid)
-{
-  if (MessageLoop::current() != mUILoop) {
-    // We have to send this message from the "UI thread" (main
-    // thread).
-    mUILoop->PostTask(NewRunnableMethod<CSSPoint,
-                                        Modifiers,
-                                        ScrollableLayerGuid>(this,
-                                                             &RemoteContentController::HandleDoubleTap,
-                                                             aPoint, aModifiers, aGuid));
-    return;
-  }
-  if (CanSend()) {
-    Unused << SendHandleDoubleTap(mBrowserParent->AdjustTapToChildWidget(aPoint),
-            aModifiers, aGuid);
-  }
-}
-
-void
-RemoteContentController::HandleSingleTap(const CSSPoint& aPoint,
-                                         Modifiers aModifiers,
-                                         const ScrollableLayerGuid& aGuid)
+RemoteContentController::HandleTap(TapType aTapType,
+                                   const CSSPoint& aPoint,
+                                   Modifiers aModifiers,
+                                   const ScrollableLayerGuid& aGuid,
+                                   uint64_t aInputBlockId)
 {
   if (MessageLoop::current() != mUILoop) {
     // We have to send this message from the "UI thread" (main
     // thread).
-    mUILoop->PostTask(NewRunnableMethod<CSSPoint,
-                                        Modifiers,
-                                        ScrollableLayerGuid>(this,
-                                                             &RemoteContentController::HandleSingleTap,
-                                                             aPoint, aModifiers, aGuid));
+    mUILoop->PostTask(NewRunnableMethod<TapType, CSSPoint, Modifiers,
+                                        ScrollableLayerGuid, uint64_t>(this,
+                                          &RemoteContentController::HandleTap,
+                                          aTapType, aPoint, aModifiers, aGuid,
+                                          aInputBlockId));
     return;
   }
 
-  bool callTakeFocusForClickFromTap;
-  layout::RenderFrameParent* frame;
-  if (mBrowserParent && (frame = mBrowserParent->GetRenderFrame()) &&
-      mLayersId == frame->GetLayersId()) {
-    // Avoid going over IPC and back for calling TakeFocusForClickFromTap,
-    // since the right RenderFrameParent is living in this process.
-    frame->TakeFocusForClickFromTap();
-    callTakeFocusForClickFromTap = false;
-  } else {
-    callTakeFocusForClickFromTap = true;
+  bool callTakeFocusForClickFromTap = (aTapType == TapType::eSingleTap);
+  if (callTakeFocusForClickFromTap && mBrowserParent) {
+    layout::RenderFrameParent* frame = mBrowserParent->GetRenderFrame();
+    if (frame && mLayersId == frame->GetLayersId()) {
+      // Avoid going over IPC and back for calling TakeFocusForClickFromTap,
+      // since the right RenderFrameParent is living in this process.
+      frame->TakeFocusForClickFromTap();
+      callTakeFocusForClickFromTap = false;
+    }
   }
 
   if (CanSend()) {
-    Unused << SendHandleSingleTap(mBrowserParent->AdjustTapToChildWidget(aPoint),
-            aModifiers, aGuid, callTakeFocusForClickFromTap);
-  }
-}
-
-void
-RemoteContentController::HandleLongTap(const CSSPoint& aPoint,
-                                       Modifiers aModifiers,
-                                       const ScrollableLayerGuid& aGuid,
-                                       uint64_t aInputBlockId)
-{
-  if (MessageLoop::current() != mUILoop) {
-    // We have to send this message from the "UI thread" (main
-    // thread).
-    mUILoop->PostTask(NewRunnableMethod<CSSPoint,
-                                        Modifiers,
-                                        ScrollableLayerGuid,
-                                        uint64_t>(this,
-                                                  &RemoteContentController::HandleLongTap,
-                                                  aPoint, aModifiers, aGuid, aInputBlockId));
-    return;
-  }
-  if (CanSend()) {
-    Unused << SendHandleLongTap(mBrowserParent->AdjustTapToChildWidget(aPoint),
-            aModifiers, aGuid, aInputBlockId);
+    Unused << SendHandleTap(aTapType, mBrowserParent->AdjustTapToChildWidget(aPoint),
+            aModifiers, aGuid, aInputBlockId, callTakeFocusForClickFromTap);
   }
 }
 
 void
 RemoteContentController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)
 {
 #ifdef MOZ_ANDROID_APZ
   AndroidBridge::Bridge()->PostTaskToUiThread(Move(aTask), aDelayMs);
--- a/gfx/layers/ipc/RemoteContentController.h
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -26,39 +26,33 @@ class APZCTreeManager;
  * GeckoContentController for a browser living in a remote process.
  * Most of the member functions can be called on any thread, exceptions are
  * annotated in comments. The PAPZ protocol runs on the main thread (so all the
  * Recv* member functions do too).
  */
 class RemoteContentController : public GeckoContentController
                               , public PAPZParent
 {
+  using GeckoContentController::TapType;
   using GeckoContentController::APZStateChange;
 
 public:
   explicit RemoteContentController(uint64_t aLayersId,
                                    dom::TabParent* aBrowserParent);
 
   virtual ~RemoteContentController();
 
   // Needs to be called on the main thread.
   virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override;
 
-  virtual void HandleDoubleTap(const CSSPoint& aPoint,
-                               Modifiers aModifiers,
-                               const ScrollableLayerGuid& aGuid) override;
-
-  virtual void HandleSingleTap(const CSSPoint& aPoint,
-                               Modifiers aModifiers,
-                               const ScrollableLayerGuid& aGuid) override;
-
-  virtual void HandleLongTap(const CSSPoint& aPoint,
-                             Modifiers aModifiers,
-                             const ScrollableLayerGuid& aGuid,
-                             uint64_t aInputBlockId) override;
+  virtual void HandleTap(TapType aTapType,
+                         const CSSPoint& aPoint,
+                         Modifiers aModifiers,
+                         const ScrollableLayerGuid& aGuid,
+                         uint64_t aInputBlockId) override;
 
   virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
 
   virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion) override;
 
   virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                     APZStateChange aChange,
                                     int aArg) override;
--- a/widget/android/AndroidContentController.cpp
+++ b/widget/android/AndroidContentController.cpp
@@ -39,26 +39,27 @@ AndroidContentController::NotifyDefaultP
             aInputBlockId, aDefaultPrevented), 0);
         return;
     }
 
     aManager->ContentReceivedInputBlock(aInputBlockId, aDefaultPrevented);
 }
 
 void
-AndroidContentController::HandleSingleTap(const CSSPoint& aPoint,
-                                          Modifiers aModifiers,
-                                          const ScrollableLayerGuid& aGuid)
+AndroidContentController::HandleTap(TapType aType, const CSSPoint& aPoint,
+                                    Modifiers aModifiers,
+                                    const ScrollableLayerGuid& aGuid,
+                                    uint64_t aInputBlockId)
 {
     // This function will get invoked first on the Java UI thread, and then
     // again on the main thread (because of the code in ChromeProcessController::
-    // HandleSingleTap). We want to post the SingleTap message once; it can be
+    // HandleTap). We want to post the SingleTap message once; it can be
     // done from either thread but we need access to the callback transform
     // so we do it from the main thread.
-    if (NS_IsMainThread()) {
+    if (NS_IsMainThread() && aType == TapType::eSingleTap) {
         CSSPoint point = mozilla::layers::APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
 
         nsIContent* content = nsLayoutUtils::FindContentFor(aGuid.mScrollId);
         nsIPresShell* shell = content
             ? mozilla::layers::APZCCallbackHelper::GetRootContentDocumentPresShellForContent(content)
             : nullptr;
 
         if (shell && shell->ScaleToResolution()) {
@@ -78,17 +79,17 @@ AndroidContentController::HandleSingleTa
             }
 
             nsPrintfCString data("{\"x\":%d,\"y\":%d}", rounded.x, rounded.y);
             obsServ->NotifyObservers(nullptr, "Gesture:SingleTap",
                                      NS_ConvertASCIItoUTF16(data).get());
         });
     }
 
-    ChromeProcessController::HandleSingleTap(aPoint, aModifiers, aGuid);
+    ChromeProcessController::HandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId);
 }
 
 void
 AndroidContentController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)
 {
     AndroidBridge::Bridge()->PostTaskToUiThread(Move(aTask), aDelayMs);
 }
 void
--- a/widget/android/AndroidContentController.h
+++ b/widget/android/AndroidContentController.h
@@ -29,19 +29,18 @@ public:
                              mozilla::layers::APZEventState* aAPZEventState,
                              mozilla::layers::APZCTreeManager* aAPZCTreeManager)
       : mozilla::layers::ChromeProcessController(aWindow, aAPZEventState, aAPZCTreeManager)
       , mAndroidWindow(aWindow)
     {}
 
     // ChromeProcessController methods
     virtual void Destroy() override;
-    void HandleSingleTap(const CSSPoint& aPoint,
-                         Modifiers aModifiers,
-                         const ScrollableLayerGuid& aGuid) override;
+    void HandleTap(TapType aType, const CSSPoint& aPoint, Modifiers aModifiers,
+                   const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId) override;
     void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
     void UpdateOverscrollVelocity(const float aX, const float aY) override;
     void UpdateOverscrollOffset(const float aX, const float aY) override;
     void SetScrollingRootContent(const bool isRootContent) override;
     void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                               APZStateChange aChange,
                               int aArg) override;