Bug 1503029 - Ensure the slider frame is notified of APZ drag initiation. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Thu, 22 Nov 2018 18:00:49 +0000
changeset 506934 b446016b76f0efd2c9feee0a9c380bc35e4f0697
parent 506933 71fd8cb4170a2934d421da9cf43f772fec5b45cd
child 506935 92a1eb79716cbf0c1524db328ddc8e5cc8c159bb
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1503029
milestone65.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 1503029 - Ensure the slider frame is notified of APZ drag initiation. r=botond This adds a notification from APZ to the scrollbar's slider frame to inform it of APZ starting an async scrollbar drag. This is useful because APZ can start a scrollbar drag and even change the scroll position before the scrollbar frame even handles the mousedown event. In such a case, the mousedown can land on where the scrollthumb *used to be* before it was dragged away. This can result in scroll-to-click behavior getting triggered and the scrollthumb glitching. With this patch, the new notification follows the same path as the request-repaint message, and so is guaranteed to arrive at the scrollbar before any request-repaint messages. It sets some state that can be used to correct the behaviour described above. Differential Revision: https://phabricator.services.mozilla.com/D12364
gfx/layers/apz/public/GeckoContentController.h
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/test/gtest/APZTestCommon.h
gfx/layers/apz/util/APZCCallbackHelper.cpp
gfx/layers/apz/util/APZCCallbackHelper.h
gfx/layers/apz/util/ChromeProcessController.cpp
gfx/layers/apz/util/ChromeProcessController.h
gfx/layers/apz/util/ContentProcessController.cpp
gfx/layers/apz/util/ContentProcessController.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
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsIScrollableFrame.h
layout/xul/nsSliderFrame.cpp
layout/xul/nsSliderFrame.h
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -3,16 +3,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/. */
 
 #ifndef mozilla_layers_GeckoContentController_h
 #define mozilla_layers_GeckoContentController_h
 
 #include "InputData.h"                  // for PinchGestureInput
+#include "LayersTypes.h"                // for ScrollDirection
 #include "Units.h"                      // for CSSPoint, CSSRect, etc
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/DefineEnum.h"         // for MOZ_DEFINE_ENUM
 #include "mozilla/EventForwards.h"      // for Modifiers
 #include "mozilla/layers/RepaintRequest.h" // for RepaintRequest
 #include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid, etc
 #include "nsISupportsImpl.h"
 
@@ -151,17 +152,29 @@ public:
   virtual void NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID& aScrollId, const nsString& aEvent)
   {}
 
   /**
    * Notify content that the repaint requests have been flushed.
    */
   virtual void NotifyFlushComplete() = 0;
 
+  /**
+   * If the async scrollbar-drag initiation code kicks in on the APZ side, then
+   * we need to let content know that we are dragging the scrollbar. Otherwise,
+   * by the time the mousedown events is handled by content, the scrollthumb
+   * could already have been moved via a RequestContentRepaint message at a
+   * new scroll position, and the mousedown might end up triggering a click-to-
+   * scroll on where the thumb used to be.
+   */
+  virtual void NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                                 const ScrollableLayerGuid::ViewID& aScrollId,
+                                                 ScrollDirection aDirection) = 0;
   virtual void NotifyAsyncScrollbarDragRejected(const ScrollableLayerGuid::ViewID& aScrollId) = 0;
+
   virtual void NotifyAsyncAutoscrollRejected(const ScrollableLayerGuid::ViewID& aScrollId) = 0;
 
   virtual void CancelAutoscroll(const ScrollableLayerGuid& aGuid) = 0;
 
   virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) {}
   virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) {}
 
   GeckoContentController() {}
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -846,16 +846,27 @@ APZCTreeManager::StopAutoscroll(const Sc
   APZThreadUtils::AssertOnControllerThread();
 
   if (RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid)) {
     apzc->StopAutoscroll();
   }
 }
 
 void
+APZCTreeManager::NotifyScrollbarDragInitiated(uint64_t aDragBlockId,
+                                              const ScrollableLayerGuid& aGuid,
+                                              ScrollDirection aDirection) const
+{
+  RefPtr<GeckoContentController> controller =
+    GetContentController(aGuid.mLayersId);
+  MOZ_ASSERT(controller);
+  controller->NotifyAsyncScrollbarDragInitiated(aDragBlockId, aGuid.mScrollId, aDirection);
+}
+
+void
 APZCTreeManager::NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const
 {
   RefPtr<GeckoContentController> controller =
     GetContentController(aGuid.mLayersId);
   MOZ_ASSERT(controller);
   controller->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId);
 }
 
@@ -1891,16 +1902,18 @@ APZCTreeManager::SetupScrollbarDrag(Mous
     dragStart -= thumbStart;
 
     // Content can't prevent scrollbar dragging with preventDefault(),
     // so we don't need to wait for a content response. It's important
     // to do this before calling ConfirmDragBlock() since that can
     // potentially process and consume the block.
     dragBlock->SetContentResponse(false);
 
+    NotifyScrollbarDragInitiated(dragBlockId, aApzc->GetGuid(), *thumbData.mDirection);
+
     mInputQueue->ConfirmDragBlock(
         dragBlockId, aApzc,
         AsyncDragMetrics(aApzc->GetGuid().mScrollId,
                          aApzc->GetGuid().mPresShellId,
                          dragBlockId,
                          dragStart,
                          *thumbData.mDirection));
   }
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -710,16 +710,19 @@ private:
                                           HitTestingTreeNode* aParent,
                                           HitTestingTreeNode* aNextSibling,
                                           TreeBuildingState& aState);
 
   template<class ScrollNode>
   void PrintAPZCInfo(const ScrollNode& aLayer,
                      const AsyncPanZoomController* apzc);
 
+  void NotifyScrollbarDragInitiated(uint64_t aDragBlockId,
+                                    const ScrollableLayerGuid& aGuid,
+                                    ScrollDirection aDirection) const;
   void NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const;
   void NotifyAutoscrollRejected(const ScrollableLayerGuid& aGuid) const;
 
   // Requires the caller to hold mTreeLock.
   LayerToParentLayerMatrix4x4 ComputeTransformForNode(const HitTestingTreeNode* aNode) const;
 
   // Returns a pointer to the GeckoContentController for the given layers id.
   already_AddRefed<GeckoContentController> GetContentController(LayersId aLayersId) const;
--- a/gfx/layers/apz/test/gtest/APZTestCommon.h
+++ b/gfx/layers/apz/test/gtest/APZTestCommon.h
@@ -113,16 +113,17 @@ public:
   bool IsRepaintThread() {
     return NS_IsMainThread();
   }
   void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) {
     NS_DispatchToMainThread(std::move(aTask));
   }
   MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
   MOCK_METHOD0(NotifyFlushComplete, void());
+  MOCK_METHOD3(NotifyAsyncScrollbarDragInitiated, void(uint64_t, const ScrollableLayerGuid::ViewID&, ScrollDirection aDirection));
   MOCK_METHOD1(NotifyAsyncScrollbarDragRejected, void(const ScrollableLayerGuid::ViewID&));
   MOCK_METHOD1(NotifyAsyncAutoscrollRejected, void(const ScrollableLayerGuid::ViewID&));
   MOCK_METHOD1(CancelAutoscroll, void(const ScrollableLayerGuid&));
 };
 
 class MockContentControllerDelayed : public MockContentController {
 public:
   MockContentControllerDelayed()
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -937,16 +937,27 @@ APZCCallbackHelper::NotifyFlushComplete(
 APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame)
 {
   return aFrame->IsProcessingAsyncScroll()
          || nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
          || aFrame->LastSmoothScrollOrigin();
 }
 
 /* static */ void
+APZCCallbackHelper::NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                                      const ScrollableLayerGuid::ViewID& aScrollId,
+                                                      ScrollDirection aDirection)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (nsIScrollableFrame* scrollFrame = nsLayoutUtils::FindScrollableFrameFor(aScrollId)) {
+    scrollFrame->AsyncScrollbarDragInitiated(aDragBlockId, aDirection);
+  }
+}
+
+/* static */ void
 APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(const ScrollableLayerGuid::ViewID& aScrollId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (nsIScrollableFrame* scrollFrame = nsLayoutUtils::FindScrollableFrameFor(aScrollId)) {
     scrollFrame->AsyncScrollbarDragRejected();
   }
 }
 
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -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/. */
 
 #ifndef mozilla_layers_APZCCallbackHelper_h
 #define mozilla_layers_APZCCallbackHelper_h
 
 #include "InputData.h"
+#include "LayersTypes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/layers/APZUtils.h"
 #include "mozilla/layers/RepaintRequest.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsRefreshDriver.h"
 
 #include <functional>
 
@@ -181,16 +182,19 @@ public:
                                                         const SetAllowedTouchBehaviorCallback& aCallback);
 
     /* Notify content of a mouse scroll testing event. */
     static void NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID& aScrollId, const nsString& aEvent);
 
     /* Notify content that the repaint flush is complete. */
     static void NotifyFlushComplete(nsIPresShell* aShell);
 
+    static void NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                                  const ScrollableLayerGuid::ViewID& aScrollId,
+                                                  ScrollDirection aDirection);
     static void NotifyAsyncScrollbarDragRejected(const ScrollableLayerGuid::ViewID& aScrollId);
     static void NotifyAsyncAutoscrollRejected(const ScrollableLayerGuid::ViewID& aScrollId);
 
     static void CancelAutoscroll(const ScrollableLayerGuid::ViewID& aScrollId);
 
     static ScreenMargin
     AdjustDisplayPortForScrollDelta(const RepaintRequest& aRequest,
                                     const CSSPoint& aActualScrollOffset);
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -297,16 +297,35 @@ void
 ChromeProcessController::NotifyFlushComplete()
 {
   MOZ_ASSERT(IsRepaintThread());
 
   APZCCallbackHelper::NotifyFlushComplete(GetPresShell());
 }
 
 void
+ChromeProcessController::NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                                           const ScrollableLayerGuid::ViewID& aScrollId,
+                                                           ScrollDirection aDirection)
+{
+  if (MessageLoop::current() != mUILoop) {
+    mUILoop->PostTask(NewRunnableMethod<uint64_t,
+                                        ScrollableLayerGuid::ViewID,
+                                        ScrollDirection>(
+      "layers::ChromeProcessController::NotifyAsyncScrollbarDragInitiated",
+      this,
+      &ChromeProcessController::NotifyAsyncScrollbarDragInitiated,
+      aDragBlockId, aScrollId, aDirection));
+    return;
+  }
+
+  APZCCallbackHelper::NotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId, aDirection);
+}
+
+void
 ChromeProcessController::NotifyAsyncScrollbarDragRejected(const ScrollableLayerGuid::ViewID& aScrollId)
 {
   if (MessageLoop::current() != mUILoop) {
     mUILoop->PostTask(NewRunnableMethod<ScrollableLayerGuid::ViewID>(
       "layers::ChromeProcessController::NotifyAsyncScrollbarDragRejected",
       this,
       &ChromeProcessController::NotifyAsyncScrollbarDragRejected,
       aScrollId));
--- a/gfx/layers/apz/util/ChromeProcessController.h
+++ b/gfx/layers/apz/util/ChromeProcessController.h
@@ -60,16 +60,19 @@ public:
                                   LayoutDeviceCoord aSpanChange,
                                   Modifiers aModifiers) override;
   virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                     APZStateChange aChange,
                                     int aArg) override;
   virtual void NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID& aScrollId,
                                          const nsString& aEvent) override;
   virtual void NotifyFlushComplete() override;
+  virtual void NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                                 const ScrollableLayerGuid::ViewID& aScrollId,
+                                                 ScrollDirection aDirection) override;
   virtual void NotifyAsyncScrollbarDragRejected(const ScrollableLayerGuid::ViewID& aScrollId) override;
   virtual void NotifyAsyncAutoscrollRejected(const ScrollableLayerGuid::ViewID& aScrollId) override;
   virtual void CancelAutoscroll(const ScrollableLayerGuid& aGuid) override;
 private:
   nsCOMPtr<nsIWidget> mWidget;
   RefPtr<APZEventState> mAPZEventState;
   RefPtr<IAPZCTreeManager> mAPZCTreeManager;
   MessageLoop* mUILoop;
--- a/gfx/layers/apz/util/ContentProcessController.cpp
+++ b/gfx/layers/apz/util/ContentProcessController.cpp
@@ -82,16 +82,24 @@ ContentProcessController::NotifyFlushCom
     if (nsCOMPtr<nsIDocument> doc = mBrowser->GetDocument()) {
       shell = doc->GetShell();
     }
     APZCCallbackHelper::NotifyFlushComplete(shell.get());
   }
 }
 
 void
+ContentProcessController::NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                                            const ScrollableLayerGuid::ViewID& aScrollId,
+                                                            ScrollDirection aDirection)
+{
+  APZCCallbackHelper::NotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId, aDirection);
+}
+
+void
 ContentProcessController::NotifyAsyncScrollbarDragRejected(const ScrollableLayerGuid::ViewID& aScrollId)
 {
   APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId);
 }
 
 void
 ContentProcessController::NotifyAsyncAutoscrollRejected(const ScrollableLayerGuid::ViewID& aScrollId)
 {
--- a/gfx/layers/apz/util/ContentProcessController.h
+++ b/gfx/layers/apz/util/ContentProcessController.h
@@ -57,16 +57,19 @@ public:
                             APZStateChange aChange,
                             int aArg) override;
 
   void NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID& aScrollId,
                                  const nsString& aEvent) override;
 
   void NotifyFlushComplete() override;
 
+  void NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                         const ScrollableLayerGuid::ViewID& aScrollId,
+                                         ScrollDirection aDirection) override;
   void NotifyAsyncScrollbarDragRejected(const ScrollableLayerGuid::ViewID& aScrollId) override;
 
   void NotifyAsyncAutoscrollRejected(const ScrollableLayerGuid::ViewID& aScrollId) override;
 
   void CancelAutoscroll(const ScrollableLayerGuid& aGuid) override;
 
   void PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs) override;
 
--- a/gfx/layers/ipc/APZChild.cpp
+++ b/gfx/layers/ipc/APZChild.cpp
@@ -74,16 +74,25 @@ APZChild::RecvNotifyFlushComplete()
 {
   MOZ_ASSERT(mController->IsRepaintThread());
 
   mController->NotifyFlushComplete();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+APZChild::RecvNotifyAsyncScrollbarDragInitiated(const uint64_t& aDragBlockId,
+                                                const ViewID& aScrollId,
+                                                const ScrollDirection& aDirection)
+{
+  mController->NotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId, aDirection);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 APZChild::RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId)
 {
   mController->NotifyAsyncScrollbarDragRejected(aScrollId);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZChild::RecvNotifyAsyncAutoscrollRejected(const ViewID& aScrollId)
--- a/gfx/layers/ipc/APZChild.h
+++ b/gfx/layers/ipc/APZChild.h
@@ -35,16 +35,19 @@ public:
                                                         const nsString& aEvent) override;
 
   mozilla::ipc::IPCResult RecvNotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                                    const APZStateChange& aChange,
                                                    const int& aArg) override;
 
   mozilla::ipc::IPCResult RecvNotifyFlushComplete() override;
 
+  mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragInitiated(const uint64_t& aDragBlockId,
+                                                                const ViewID& aScrollId,
+                                                                const ScrollDirection& aDirection) override;
   mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId) override;
 
   mozilla::ipc::IPCResult RecvNotifyAsyncAutoscrollRejected(const ViewID& aScrollId) override;
 
   mozilla::ipc::IPCResult RecvDestroy() override;
 
 private:
   RefPtr<GeckoContentController> mController;
--- a/gfx/layers/ipc/PAPZ.ipdl
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -11,16 +11,17 @@ include "mozilla/layers/LayersMessageUti
 include protocol PCompositorBridge;
 
 using CSSRect from "Units.h";
 using struct mozilla::layers::RepaintRequest from "mozilla/layers/RepaintRequest.h";
 using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
 using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
 using mozilla::layers::MaybeZoomConstraints from "mozilla/layers/ZoomConstraints.h";
 using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h";
+using mozilla::layers::ScrollDirection from "mozilla/layers/LayersTypes.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 class nsRegion from "nsRegion.h";
 
 namespace mozilla {
 namespace layers {
 
@@ -57,16 +58,18 @@ child:
   async UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent);
 
   async NotifyMozMouseScrollEvent(ViewID aScrollId, nsString aEvent);
 
   async NotifyAPZStateChange(ScrollableLayerGuid aGuid, APZStateChange aChange, int aArg);
 
   async NotifyFlushComplete();
 
+  async NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId, ViewID aScrollId, ScrollDirection aDirection);
+
   async NotifyAsyncScrollbarDragRejected(ViewID aScrollId);
 
   async NotifyAsyncAutoscrollRejected(ViewID aScrollId);
 
   async Destroy();
 };
 
 } // layers
--- a/gfx/layers/ipc/RemoteContentController.cpp
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -299,16 +299,38 @@ RemoteContentController::NotifyFlushComp
   MOZ_ASSERT(IsRepaintThread());
 
   if (mCanSend) {
     Unused << SendNotifyFlushComplete();
   }
 }
 
 void
+RemoteContentController::NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                                           const ScrollableLayerGuid::ViewID& aScrollId,
+                                                           ScrollDirection aDirection)
+{
+  if (MessageLoop::current() != mCompositorThread) {
+    // We have to send messages from the compositor thread
+    mCompositorThread->PostTask(NewRunnableMethod<uint64_t,
+                                                  ScrollableLayerGuid::ViewID,
+                                                  ScrollDirection>(
+      "layers::RemoteContentController::NotifyAsyncScrollbarDragInitiated",
+      this,
+      &RemoteContentController::NotifyAsyncScrollbarDragInitiated,
+      aDragBlockId, aScrollId, aDirection));
+    return;
+  }
+
+  if (mCanSend) {
+    Unused << SendNotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId, aDirection);
+  }
+}
+
+void
 RemoteContentController::NotifyAsyncScrollbarDragRejected(const ScrollableLayerGuid::ViewID& aScrollId)
 {
   if (MessageLoop::current() != mCompositorThread) {
     // We have to send messages from the compositor thread
     mCompositorThread->PostTask(NewRunnableMethod<ScrollableLayerGuid::ViewID>(
       "layers::RemoteContentController::NotifyAsyncScrollbarDragRejected",
       this,
       &RemoteContentController::NotifyAsyncScrollbarDragRejected,
--- a/gfx/layers/ipc/RemoteContentController.h
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -66,16 +66,19 @@ public:
 
   virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) override;
 
   virtual void NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID& aScrollId,
                                          const nsString& aEvent) override;
 
   virtual void NotifyFlushComplete() override;
 
+  virtual void NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                                 const ScrollableLayerGuid::ViewID& aScrollId,
+                                                 ScrollDirection aDirection) override;
   virtual void NotifyAsyncScrollbarDragRejected(const ScrollableLayerGuid::ViewID& aScrollId) override;
 
   virtual void NotifyAsyncAutoscrollRejected(const ScrollableLayerGuid::ViewID& aScrollId) override;
 
   virtual void CancelAutoscroll(const ScrollableLayerGuid& aScrollId) override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -6680,32 +6680,63 @@ ScrollFrameHelper::DragScroll(WidgetEven
 
   if (offset.x || offset.y) {
     ScrollTo(GetScrollPosition() + offset, nsIScrollableFrame::NORMAL, nsGkAtoms::other);
   }
 
   return willScroll;
 }
 
-static void
-AsyncScrollbarDragRejected(nsIFrame* aScrollbar)
-{
-  if (!aScrollbar) {
-    return;
-  }
-
-  for (nsIFrame::ChildListIterator childLists(aScrollbar);
+static nsSliderFrame*
+GetSliderFrame(nsIFrame* aScrollbarFrame)
+{
+  if (!aScrollbarFrame) {
+    return nullptr;
+  }
+
+  for (nsIFrame::ChildListIterator childLists(aScrollbarFrame);
        !childLists.IsDone();
        childLists.Next()) {
     for (nsIFrame* frame : childLists.CurrentList()) {
       if (nsSliderFrame* sliderFrame = do_QueryFrame(frame)) {
-        sliderFrame->AsyncScrollbarDragRejected();
+        return sliderFrame;
       }
     }
   }
+  return nullptr;
+}
+
+static void
+AsyncScrollbarDragInitiated(uint64_t aDragBlockId, nsIFrame* aScrollbar)
+{
+  if (nsSliderFrame* sliderFrame = GetSliderFrame(aScrollbar)) {
+    sliderFrame->AsyncScrollbarDragInitiated(aDragBlockId);
+  }
+}
+
+void
+ScrollFrameHelper::AsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                               ScrollDirection aDirection)
+{
+  switch (aDirection) {
+    case ScrollDirection::eVertical:
+      ::AsyncScrollbarDragInitiated(aDragBlockId, mVScrollbarBox);
+      break;
+    case ScrollDirection::eHorizontal:
+      ::AsyncScrollbarDragInitiated(aDragBlockId, mHScrollbarBox);
+      break;
+  }
+}
+
+static void
+AsyncScrollbarDragRejected(nsIFrame* aScrollbar)
+{
+  if (nsSliderFrame* sliderFrame = GetSliderFrame(aScrollbar)) {
+    sliderFrame->AsyncScrollbarDragRejected();
+  }
 }
 
 void
 ScrollFrameHelper::AsyncScrollbarDragRejected()
 {
   // We don't get told which scrollbar requested the async drag,
   // so we notify both.
   ::AsyncScrollbarDragRejected(mHScrollbarBox);
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -503,16 +503,18 @@ public:
                     nsIScrollbarMediator::ScrollSnapMode aSnap
                       = nsIScrollbarMediator::DISABLE_SNAP);
   bool ShouldSuppressScrollbarRepaints() const {
     return mSuppressScrollbarRepaints;
   }
 
   bool DragScroll(WidgetEvent* aEvent);
 
+  void AsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                   mozilla::layers::ScrollDirection aDirection);
   void AsyncScrollbarDragRejected();
 
   bool IsRootScrollFrameOfDocument() const { return mIsRoot; }
 
   // owning references to the nsIAnonymousContentCreator-built content
   nsCOMPtr<mozilla::dom::Element> mHScrollbarContent;
   nsCOMPtr<mozilla::dom::Element> mVScrollbarContent;
   nsCOMPtr<mozilla::dom::Element> mScrollCornerContent;
@@ -1110,16 +1112,21 @@ public:
   ScrollSnapInfo GetScrollSnapInfo() const override {
     return mHelper.GetScrollSnapInfo();
   }
 
   virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override {
     return mHelper.DragScroll(aEvent);
   }
 
+  virtual void AsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                           mozilla::layers::ScrollDirection aDirection) override {
+    return mHelper.AsyncScrollbarDragInitiated(aDragBlockId, aDirection);
+  }
+
   virtual void AsyncScrollbarDragRejected() override {
     return mHelper.AsyncScrollbarDragRejected();
   }
 
   virtual bool IsRootScrollFrameOfDocument() const override {
     return mHelper.IsRootScrollFrameOfDocument();
   }
 
@@ -1574,16 +1581,21 @@ public:
   ScrollSnapInfo GetScrollSnapInfo() const override {
     return mHelper.GetScrollSnapInfo();
   }
 
   virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override {
     return mHelper.DragScroll(aEvent);
   }
 
+  virtual void AsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                           mozilla::layers::ScrollDirection aDirection) override {
+    return mHelper.AsyncScrollbarDragInitiated(aDragBlockId, aDirection);
+  }
+
   virtual void AsyncScrollbarDragRejected() override {
     return mHelper.AsyncScrollbarDragRejected();
   }
 
   virtual bool IsRootScrollFrameOfDocument() const override {
     return mHelper.IsRootScrollFrameOfDocument();
   }
 
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -531,16 +531,18 @@ public:
   /**
    * Given the drag event aEvent, determine whether the mouse is near the edge
    * of the scrollable area, and scroll the view in the direction of that edge
    * if so. If scrolling occurred, true is returned. When false is returned, the
    * caller should look for an ancestor to scroll.
    */
   virtual bool DragScroll(mozilla::WidgetEvent* aEvent) = 0;
 
+  virtual void AsyncScrollbarDragInitiated(uint64_t aDragBlockId,
+                                           mozilla::layers::ScrollDirection aDirection) = 0;
   virtual void AsyncScrollbarDragRejected() = 0;
 
   /**
    * Returns whether this scroll frame is the root scroll frame of the document
    * that it is in. Note that some documents don't have root scroll frames at
    * all (ie XUL documents) even though they may contain other scroll frames.
    */
   virtual bool IsRootScrollFrameOfDocument() const = 0;
--- a/layout/xul/nsSliderFrame.cpp
+++ b/layout/xul/nsSliderFrame.cpp
@@ -498,16 +498,32 @@ nsSliderFrame::DoXULLayout(nsBoxLayoutSt
 
 nsresult
 nsSliderFrame::HandleEvent(nsPresContext* aPresContext,
                            WidgetGUIEvent* aEvent,
                            nsEventStatus* aEventStatus)
 {
   NS_ENSURE_ARG_POINTER(aEventStatus);
 
+  if (mAPZDragInitiated &&
+      *mAPZDragInitiated == InputAPZContext::GetInputBlockId() &&
+      aEvent->mMessage == eMouseDown) {
+    // If we get the mousedown after the APZ notification, then immediately
+    // switch into the state corresponding to an APZ thumb-drag. Note that
+    // we can't just do this in AsyncScrollbarDragInitiated() directly because
+    // the handling for this mousedown event in the presShell will reset the
+    // capturing content which makes isDraggingThumb() return false. We check
+    // the input block here to make sure that we correctly handle any ordering
+    // of {eMouseDown arriving, AsyncScrollbarDragInitiated() being called}.
+    mAPZDragInitiated = Nothing();
+    DragThumb(true);
+    mScrollingWithAPZ = true;
+    return NS_OK;
+  }
+
   // If a web page calls event.preventDefault() we still want to
   // scroll when scroll arrow is clicked. See bug 511075.
   if (!mContent->IsInNativeAnonymousSubtree() &&
       nsEventStatus_eConsumeNoDefault == *aEventStatus) {
     return NS_OK;
   }
 
   if (!mDragFinished && !isDraggingThumb()) {
@@ -1525,16 +1541,22 @@ nsSliderFrame::GetThumbRatio() const
   // mRatio is in thumb app units per scrolled css pixels. Convert it to a
   // ratio of the thumb's CSS pixels per scrolled CSS pixels. (Note the thumb
   // is in the scrollframe's parent's space whereas the scrolled CSS pixels
   // are in the scrollframe's space).
   return mRatio / mozilla::AppUnitsPerCSSPixel();
 }
 
 void
+nsSliderFrame::AsyncScrollbarDragInitiated(uint64_t aDragBlockId)
+{
+  mAPZDragInitiated = Some(aDragBlockId);
+}
+
+void
 nsSliderFrame::AsyncScrollbarDragRejected()
 {
   mScrollingWithAPZ = false;
   // Only suppress the displayport if we're still dragging the thumb.
   // Otherwise, no one will unsuppress it.
   if (isDraggingThumb()) {
     SuppressDisplayport();
   }
--- a/layout/xul/nsSliderFrame.h
+++ b/layout/xul/nsSliderFrame.h
@@ -128,17 +128,22 @@ public:
   NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
                            mozilla::WidgetGUIEvent* aEvent,
                            nsEventStatus* aEventStatus) override;
 
   // Return the ratio the scrollbar thumb should move in proportion to the
   // scrolled frame.
   float GetThumbRatio() const;
 
-  // Notify the slider frame than an async scrollbar drag requested in
+  // Notify the slider frame that an async scrollbar drag was started on the
+  // APZ side without consulting the main thread. The block id is the APZ
+  // input block id of the mousedown that started the drag.
+  void AsyncScrollbarDragInitiated(uint64_t aDragBlockId);
+
+  // Notify the slider frame that an async scrollbar drag requested in
   // StartAPZDrag() was rejected by APZ, and the slider frame should
   // fall back to main-thread dragging.
   void AsyncScrollbarDragRejected();
 
   bool OnlySystemGroupDispatch(mozilla::EventMessage aMessage) const override;
 
   // Returns the associated scrollframe that contains this slider if any.
   nsIScrollableFrame* GetScrollFrame();
@@ -205,13 +210,20 @@ private:
   // to process these events then the scroll position update would conflict
   // causing the scroll position to jump.
   bool mScrollingWithAPZ;
 
   // true if displayport suppression is active, for more performant
   // scrollbar-dragging behaviour.
   bool mSuppressionActive;
 
+  // If APZ initiated a scrollbar drag without main-thread involvement, it
+  // notifies us and this variable stores the input block id of the APZ input
+  // block that started the drag. This lets us handle the corresponding
+  // mousedown event properly, if it arrives after the scroll position has
+  // been shifted due to async scrollbar drag.
+  Maybe<uint64_t> mAPZDragInitiated;
+
   static bool gMiddlePref;
   static int32_t gSnapMultiplier;
 }; // class nsSliderFrame
 
 #endif