Bug 1211612 - Add DragInputBlock for async scrollbars. r=kats a=jocheng
authorBenoit Girard <b56girard@gmail.com>
Fri, 25 Mar 2016 16:25:13 +0800
changeset 292691 fddb21c5334d
parent 292690 f5896e345150
child 292692 373f76c006f2
push id328
push usergachen@mozilla.com
push dateFri, 25 Mar 2016 08:27:44 +0000
reviewerskats, jocheng
bugs1211612
milestone44.0
Bug 1211612 - Add DragInputBlock for async scrollbars. r=kats a=jocheng MozReview-Commit-ID: DtrU48en1Eu
dom/ipc/TabChild.cpp
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
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/layers/apz/util/APZEventState.cpp
gfx/layers/apz/util/APZEventState.h
layout/xul/nsSliderFrame.cpp
layout/xul/nsSliderFrame.h
widget/cocoa/nsChildView.mm
widget/gtk/nsWindow.cpp
widget/nsBaseWidget.cpp
widget/windows/nsWindow.cpp
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1791,16 +1791,20 @@ TabChild::RecvRealMouseButtonEvent(const
                                    const uint64_t& aInputBlockId)
 {
   nsEventStatus unused;
   InputAPZContext context(aGuid, aInputBlockId, unused);
 
   WidgetMouseEvent localEvent(event);
   localEvent.widget = mPuppetWidget;
   APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+
+  if (event.mFlags.mHandledByAPZ) {
+    mAPZEventState->ProcessMouseEvent(event, aGuid, aInputBlockId);
+  }
   return true;
 }
 
 bool
 TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
                               const ScrollableLayerGuid& aGuid,
                               const uint64_t& aInputBlockId)
 {
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -359,19 +359,18 @@ APZCTreeManager::StartScrollbarDrag(cons
                                     const AsyncDragMetrics& aDragMetrics)
 {
 
   RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   if (!apzc) {
     return;
   }
 
-  // TODO Confirm the input block
-  //uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber;
-  //mInputQueue->SetConfirmedMouseBlock(inputBlockId, apzc, aDragMetrics);
+  uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber;
+  mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
 }
 
 HitTestingTreeNode*
 APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
                                      const FrameMetrics& aMetrics,
                                      uint64_t aLayersId,
                                      const gfx::Matrix4x4& aAncestorTransform,
                                      HitTestingTreeNode* aParent,
@@ -694,16 +693,23 @@ APZCTreeManager::ReceiveInputEvent(Input
       touchInput.mHandledByAPZ = true;
       result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
       break;
     } case MOUSE_INPUT: {
       MouseInput& mouseInput = aEvent.AsMouseInput();
 
       RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(mouseInput.mOrigin,
                                                             &hitResult);
+
+      // When the mouse is outside the window we still want to handle dragging
+      // but we won't find an APZC. Fallback to root APZC then.
+      if (!apzc) {
+        apzc = mRootNode->GetApzc();
+      }
+
       if (apzc) {
         result = mInputQueue->ReceiveInputEvent(
           apzc,
           /* aTargetConfirmed = */ false,
           mouseInput, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
@@ -1047,16 +1053,17 @@ APZCTreeManager::ProcessMouseEvent(Widge
 {
   MouseInput input(aEvent);
   input.mOrigin = ScreenPoint(aEvent.refPoint.x, aEvent.refPoint.y);
 
   nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
 
   aEvent.refPoint.x = input.mOrigin.x;
   aEvent.refPoint.y = input.mOrigin.y;
+  aEvent.mFlags.mHandledByAPZ = true;
   return status;
 }
 
 nsEventStatus
 APZCTreeManager::ProcessWheelEvent(WidgetWheelEvent& aEvent,
                                    ScrollableLayerGuid* aOutTargetGuid,
                                    uint64_t* aOutInputBlockId)
 {
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1002,22 +1002,27 @@ static float GetAxisScale(AsyncDragMetri
   } else {
     return aValue.yScale;
   }
 }
 
 nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent,
                                                       const AsyncDragMetrics& aDragMetrics)
 {
+  if (!GetApzcTreeManager()) {
+    return nsEventStatus_eConsumeNoDefault;
+  }
+
   RefPtr<HitTestingTreeNode> node =
     GetApzcTreeManager()->FindScrollNode(aDragMetrics);
   if (!node) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
+  ReentrantMonitorAutoEnter lock(mMonitor);
   CSSPoint scrollFramePoint = aEvent.mLocalOrigin / GetFrameMetrics().GetZoom();
   // The scrollbar can be transformed with the frame but the pres shell
   // resolution is only applied to the scroll frame.
   CSSPoint scrollbarPoint = scrollFramePoint * GetFrameMetrics().GetPresShellResolution();
   CSSRect cssCompositionBound = GetFrameMetrics().GetCompositionBounds() / GetFrameMetrics().GetZoom();
 
   float mousePosition = GetAxisStart(aDragMetrics.mDirection, scrollbarPoint) -
                         aDragMetrics.mScrollbarDragOffset -
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -2,16 +2,17 @@
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* 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 "InputBlockState.h"
 #include "AsyncPanZoomController.h"         // for AsyncPanZoomController
 #include "gfxPrefs.h"                       // for gfxPrefs
+#include "mozilla/MouseEvents.h"
 #include "mozilla/SizePrintfMacros.h"       // for PRIuSIZE
 #include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
 #include "OverscrollHandoffState.h"
 
 #define TBS_LOG(...)
 // #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
 
 namespace mozilla {
@@ -153,16 +154,94 @@ CancelableBlockState::DispatchImmediate(
 }
 
 void
 CancelableBlockState::DispatchEvent(const InputData& aEvent) const
 {
   GetTargetApzc()->HandleInputEvent(aEvent, mTransformToApzc);
 }
 
+DragBlockState::DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+                               bool aTargetConfirmed,
+                               const MouseInput& aInitialEvent)
+  : CancelableBlockState(aTargetApzc, aTargetConfirmed)
+  , mReceivedMouseUp(false)
+{
+}
+
+bool
+DragBlockState::HasReceivedMouseUp()
+{
+  return mReceivedMouseUp;
+}
+
+void
+DragBlockState::MarkMouseUpReceived()
+{
+  mReceivedMouseUp = true;
+}
+
+void
+DragBlockState::SetDragMetrics(const AsyncDragMetrics& aDragMetrics)
+{
+  mDragMetrics = aDragMetrics;
+}
+
+void
+DragBlockState::DispatchEvent(const InputData& aEvent) const
+{
+  MouseInput mouseInput = aEvent.AsMouseInput();
+  if (!mouseInput.TransformToLocal(mTransformToApzc)) {
+    return;
+  }
+
+  GetTargetApzc()->HandleDragEvent(mouseInput, mDragMetrics);
+}
+
+void
+DragBlockState::AddEvent(const MouseInput& aEvent)
+{
+  mEvents.AppendElement(aEvent);
+}
+
+bool
+DragBlockState::HasEvents() const
+{
+  return !mEvents.IsEmpty();
+}
+
+void
+DragBlockState::DropEvents()
+{
+  TBS_LOG("%p dropping %" PRIuSIZE " events\n", this, mEvents.Length());
+  mEvents.Clear();
+}
+
+void
+DragBlockState::HandleEvents()
+{
+  while (HasEvents()) {
+    TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
+    MouseInput event = mEvents[0];
+    mEvents.RemoveElementAt(0);
+    DispatchEvent(event);
+  }
+}
+
+bool
+DragBlockState::MustStayActive()
+{
+  return !mReceivedMouseUp;
+}
+
+const char*
+DragBlockState::Type()
+{
+  return "drag";
+}
 // This is used to track the current wheel transaction.
 static uint64_t sLastWheelBlockId = InputBlockState::NO_BLOCK_ID;
 
 WheelBlockState::WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
                                  bool aTargetConfirmed,
                                  const ScrollWheelInput& aInitialEvent)
   : CancelableBlockState(aTargetApzc, aTargetConfirmed)
   , mTransactionEnded(false)
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -4,27 +4,29 @@
  * 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_InputBlockState_h
 #define mozilla_layers_InputBlockState_h
 
 #include "InputData.h"                      // for MultiTouchInput
 #include "mozilla/gfx/Matrix.h"             // for Matrix4x4
+#include "mozilla/layers/AsyncDragMetrics.h"
 #include "nsAutoPtr.h"                      // for nsRefPtr
 #include "nsTArray.h"                       // for nsTArray
 
 namespace mozilla {
 namespace layers {
 
 class AsyncPanZoomController;
 class OverscrollHandoffChain;
 class CancelableBlockState;
 class TouchBlockState;
 class WheelBlockState;
+class DragBlockState;
 class PanGestureBlockState;
 
 /**
  * A base class that stores state common to various input blocks.
  * Currently, it just stores the overscroll handoff chain.
  * Note that the InputBlockState constructor acquires the tree lock, so callers
  * from inside AsyncPanZoomController should ensure that the APZC lock is not
  * held.
@@ -81,16 +83,19 @@ public:
                        bool aTargetConfirmed);
 
   virtual TouchBlockState *AsTouchBlock() {
     return nullptr;
   }
   virtual WheelBlockState *AsWheelBlock() {
     return nullptr;
   }
+  virtual DragBlockState *AsDragBlock() {
+    return nullptr;
+  }
   virtual PanGestureBlockState *AsPanGestureBlock() {
     return nullptr;
   }
 
   /**
    * Record whether or not content cancelled this block of events.
    * @param aPreventDefault true iff the block is cancelled.
    * @return false if this block has already received a response from
@@ -247,16 +252,50 @@ protected:
 private:
   nsTArray<ScrollWheelInput> mEvents;
   TimeStamp mLastEventTime;
   TimeStamp mLastMouseMove;
   bool mTransactionEnded;
 };
 
 /**
+ * A block of mouse events that are part of a drag
+ */
+class DragBlockState : public CancelableBlockState
+{
+public:
+  DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+                 bool aTargetConfirmed,
+                 const MouseInput& aEvent);
+
+  bool HasEvents() const override;
+  void DropEvents() override;
+  void HandleEvents() override;
+  bool MustStayActive() override;
+  const char* Type() override;
+
+  bool HasReceivedMouseUp();
+  void MarkMouseUpReceived();
+
+  void AddEvent(const MouseInput& aEvent);
+
+  DragBlockState *AsDragBlock() override {
+    return this;
+  }
+
+  void SetDragMetrics(const AsyncDragMetrics& aDragMetrics);
+
+  void DispatchEvent(const InputData& aEvent) const override;
+private:
+  nsTArray<MouseInput> mEvents;
+  AsyncDragMetrics mDragMetrics;
+  bool mReceivedMouseUp;
+};
+
+/**
  * A single block of pan gesture events.
  */
 class PanGestureBlockState : public CancelableBlockState
 {
 public:
   PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
                        bool aTargetConfirmed,
                        const PanGestureInput& aEvent);
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -45,16 +45,21 @@ InputQueue::ReceiveInputEvent(const RefP
       return ReceiveScrollWheelInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
     }
 
     case PANGESTURE_INPUT: {
       const PanGestureInput& event = aEvent.AsPanGestureInput();
       return ReceivePanGestureInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
     }
 
+    case MOUSE_INPUT: {
+      const MouseInput& event = aEvent.AsMouseInput();
+      return ReceiveMouseInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
+    }
+
     default:
       // The return value for non-touch input is only used by tests, so just pass
       // through the return value for now. This can be changed later if needed.
       // TODO (bug 1098430): we will eventually need to have smarter handling for
       // non-touch events as well.
       return aTarget->HandleInputEvent(aEvent, aTarget->GetTransformToThis());
   }
 }
@@ -167,16 +172,79 @@ InputQueue::ReceiveTouchInput(const RefP
   }
   if (!MaybeHandleCurrentBlock(block, aEvent)) {
     block->AddEvent(aEvent.AsMultiTouchInput());
   }
   return result;
 }
 
 nsEventStatus
+InputQueue::ReceiveMouseInput(const RefPtr<AsyncPanZoomController>& aTarget,
+                              bool aTargetConfirmed,
+                              const MouseInput& aEvent,
+                              uint64_t* aOutInputBlockId) {
+  MOZ_ASSERT(!aTargetConfirmed); // We wont know the target until content tells us
+
+  // On a new mouse down we can have a new target so we must force a new block
+  // with a new target.
+  bool newBlock = aEvent.mType == MouseInput::MOUSE_DOWN && aEvent.IsLeftButton();
+
+  DragBlockState* block = nullptr;
+  if (!newBlock && !mInputBlockQueue.IsEmpty()) {
+    block = mInputBlockQueue.LastElement()->AsDragBlock();
+  }
+
+  if (block && block->HasReceivedMouseUp()) {
+    block = nullptr;
+  }
+
+  if (!newBlock && !block) {
+    return nsEventStatus_eConsumeDoDefault;
+  }
+
+  if (!block) {
+    MOZ_ASSERT(newBlock);
+    block = new DragBlockState(aTarget, aTargetConfirmed, aEvent);
+    if (aOutInputBlockId) {
+      *aOutInputBlockId = block->GetBlockId();
+    }
+
+    INPQ_LOG("started new drag block %p for target %p\n", block, aTarget.get());
+
+    SweepDepletedBlocks();
+    mInputBlockQueue.AppendElement(block);
+
+    CancelAnimationsForNewBlock(block);
+    MaybeRequestContentResponse(aTarget, block);
+
+    block->AddEvent(aEvent.AsMouseInput());
+
+    // The first event will confirm the block or not.
+    return nsEventStatus_eConsumeDoDefault;
+  }
+
+  if (aOutInputBlockId) {
+    *aOutInputBlockId = block->GetBlockId();
+  }
+
+  if (!MaybeHandleCurrentBlock(block, aEvent)) {
+    block->AddEvent(aEvent.AsMouseInput());
+  }
+
+  bool mouseUp = aEvent.mType == MouseInput::MOUSE_UP && aEvent.IsLeftButton();
+  if (mouseUp) {
+    block->MarkMouseUpReceived();
+  }
+
+  // If we're not the first event then we need to wait for the confirmation of
+  // the block.
+  return nsEventStatus_eConsumeDoDefault;
+}
+
+nsEventStatus
 InputQueue::ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarget,
                                     bool aTargetConfirmed,
                                     const ScrollWheelInput& aEvent,
                                     uint64_t* aOutInputBlockId) {
   WheelBlockState* block = nullptr;
   if (!mInputBlockQueue.IsEmpty()) {
     block = mInputBlockQueue.LastElement()->AsWheelBlock();
 
@@ -407,16 +475,24 @@ InputQueue::CurrentTouchBlock() const
 WheelBlockState*
 InputQueue::CurrentWheelBlock() const
 {
   WheelBlockState* block = CurrentBlock()->AsWheelBlock();
   MOZ_ASSERT(block);
   return block;
 }
 
+DragBlockState*
+InputQueue::CurrentDragBlock() const
+{
+  DragBlockState* block = CurrentBlock()->AsDragBlock();
+  MOZ_ASSERT(block);
+  return block;
+}
+
 PanGestureBlockState*
 InputQueue::CurrentPanGestureBlock() const
 {
   PanGestureBlockState* block = CurrentBlock()->AsPanGestureBlock();
   MOZ_ASSERT(block);
   return block;
 }
 
@@ -515,16 +591,38 @@ InputQueue::SetConfirmedTargetApzc(uint6
     }
   }
   if (success) {
     ProcessInputBlocks();
   }
 }
 
 void
+InputQueue::ConfirmDragBlock(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc,
+                                   const AsyncDragMetrics& aDragMetrics)
+{
+  APZThreadUtils::AssertOnControllerThread();
+
+  INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
+    aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
+  bool success = false;
+  for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
+    DragBlockState* block = mInputBlockQueue[i]->AsDragBlock();
+    if (block && block->GetBlockId() == aInputBlockId) {
+      block->SetDragMetrics(aDragMetrics);
+      success = block->SetConfirmedTargetApzc(aTargetApzc);
+      break;
+    }
+  }
+  if (success) {
+    ProcessInputBlocks();
+  }
+}
+
+void
 InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
   APZThreadUtils::AssertOnControllerThread();
 
   INPQ_LOG("got allowed touch behaviours; block=%" PRIu64 "\n", aInputBlockId);
   bool success = false;
   for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
     if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
       TouchBlockState *block = mInputBlockQueue[i]->AsTouchBlock();
--- a/gfx/layers/apz/src/InputQueue.h
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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_InputQueue_h
 #define mozilla_layers_InputQueue_h
 
+#include "APZUtils.h"
+#include "InputData.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/UniquePtr.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
 class InputData;
@@ -18,17 +20,19 @@ class MultiTouchInput;
 class ScrollWheelInput;
 
 namespace layers {
 
 class AsyncPanZoomController;
 class CancelableBlockState;
 class TouchBlockState;
 class WheelBlockState;
+class DragBlockState;
 class PanGestureBlockState;
+class AsyncDragMetrics;
 
 /**
  * This class stores incoming input events, separated into "input blocks", until
  * they are ready for handling. Currently input blocks are only created from
  * touch input.
  */
 class InputQueue {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InputQueue)
@@ -56,16 +60,23 @@ public:
    * This function should be invoked to notify the InputQueue once the target
    * APZC to handle an input block has been confirmed. In practice this should
    * generally be decidable upon receipt of the input event, but in some cases
    * we may need to query the layout engine to know for sure. The input block
    * this applies to should be specified via the |aInputBlockId| parameter.
    */
   void SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc);
   /**
+   * This function is invoked to confirm that the drag block should be handled
+   * by the APZ.
+   */
+  void ConfirmDragBlock(uint64_t aInputBlockId,
+                        const RefPtr<AsyncPanZoomController>& aTargetApzc,
+                        const AsyncDragMetrics& aDragMetrics);
+  /**
    * This function should be invoked to notify the InputQueue of the touch-
    * 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(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors);
   /**
@@ -82,16 +93,17 @@ public:
   CancelableBlockState* CurrentBlock() const;
   /**
    * Returns the current pending input block as a specific kind of block.
    * These methods must only be called if the current pending block is of the
    * requested type.
    */
   TouchBlockState* CurrentTouchBlock() const;
   WheelBlockState* CurrentWheelBlock() const;
+  DragBlockState* CurrentDragBlock() const;
   PanGestureBlockState* CurrentPanGestureBlock() const;
   /**
    * Returns true iff the pending block at the head of the queue is ready for
    * handling.
    */
   bool HasReadyTouchBlock() const;
   /**
    * If there is a wheel transaction, returns the WheelBlockState representing
@@ -125,16 +137,20 @@ private:
    */
   void MaybeRequestContentResponse(const RefPtr<AsyncPanZoomController>& aTarget,
                                    CancelableBlockState* aBlock);
 
   nsEventStatus ReceiveTouchInput(const RefPtr<AsyncPanZoomController>& aTarget,
                                   bool aTargetConfirmed,
                                   const MultiTouchInput& aEvent,
                                   uint64_t* aOutInputBlockId);
+  nsEventStatus ReceiveMouseInput(const RefPtr<AsyncPanZoomController>& aTarget,
+                                  bool aTargetConfirmed,
+                                  const MouseInput& aEvent,
+                                  uint64_t* aOutInputBlockId);
   nsEventStatus ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarget,
                                         bool aTargetConfirmed,
                                         const ScrollWheelInput& aEvent,
                                         uint64_t* aOutInputBlockId);
   nsEventStatus ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget,
                                         bool aTargetConfirmed,
                                         const PanGestureInput& aEvent,
                                         uint64_t* aOutInputBlockId);
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -335,16 +335,27 @@ APZEventState::ProcessWheelEvent(const W
   // If this event starts a swipe, indicate that it shouldn't result in a
   // scroll by setting defaultPrevented to true.
   bool defaultPrevented =
     aEvent.mFlags.mDefaultPrevented || aEvent.TriggersSwipe();
   mContentReceivedInputBlockCallback(aGuid, aInputBlockId, defaultPrevented);
 }
 
 void
+APZEventState::ProcessMouseEvent(const WidgetMouseEvent& aEvent,
+                                 const ScrollableLayerGuid& aGuid,
+                                 uint64_t aInputBlockId)
+{
+  // If we get here and the input block has not been confirmed then
+  // no scrollbar reacted to the event thus APZC should ignore this block.
+  bool defaultPrevented = false;
+  mContentReceivedInputBlockCallback(aGuid, aInputBlockId, defaultPrevented);
+}
+
+void
 APZEventState::ProcessAPZStateChange(const nsCOMPtr<nsIDocument>& aDocument,
                                      ViewID aViewId,
                                      APZStateChange aChange,
                                      int aArg)
 {
   switch (aChange)
   {
   case APZStateChange::TransformBegin:
--- a/gfx/layers/apz/util/APZEventState.h
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -56,16 +56,19 @@ public:
                       uint64_t aInputBlockId);
   void ProcessTouchEvent(const WidgetTouchEvent& aEvent,
                          const ScrollableLayerGuid& aGuid,
                          uint64_t aInputBlockId,
                          nsEventStatus aApzResponse);
   void ProcessWheelEvent(const WidgetWheelEvent& aEvent,
                          const ScrollableLayerGuid& aGuid,
                          uint64_t aInputBlockId);
+  void ProcessMouseEvent(const WidgetMouseEvent& aEvent,
+                         const ScrollableLayerGuid& aGuid,
+                         uint64_t aInputBlockId);
   void ProcessAPZStateChange(const nsCOMPtr<nsIDocument>& aDocument,
                              ViewID aViewId,
                              APZStateChange aChange,
                              int aArg);
 private:
   ~APZEventState();
   bool SendPendingTouchPreventedResponse(bool aPreventDefault);
   already_AddRefed<nsIWidget> GetWidget() const;
--- a/layout/xul/nsSliderFrame.cpp
+++ b/layout/xul/nsSliderFrame.cpp
@@ -69,17 +69,18 @@ NS_QUERYFRAME_HEAD(nsSliderFrame)
   NS_QUERYFRAME_ENTRY(nsSliderFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
 
 nsSliderFrame::nsSliderFrame(nsStyleContext* aContext):
   nsBoxFrame(aContext),
   mCurPos(0),
   mChange(0),
   mDragFinished(true),
-  mUserChanged(false)
+  mUserChanged(false),
+  mScrollingWithAPZ(false)
 {
 }
 
 // stop timer
 nsSliderFrame::~nsSliderFrame()
 {
 }
 
@@ -480,16 +481,19 @@ nsSliderFrame::HandleEvent(nsPresContext
   scrollbar = GetContentOfBox(scrollbarBox);
   bool isHorizontal = IsHorizontal();
 
   if (isDraggingThumb())
   {
     switch (aEvent->mMessage) {
     case eTouchMove:
     case eMouseMove: {
+      if (mScrollingWithAPZ) {
+        break;
+      }
       nsPoint eventPoint;
       if (!GetEventPoint(aEvent, eventPoint)) {
         break;
       }
       if (mChange) {
         // On Linux the destination point is determined by the initial click
         // on the scrollbar track and doesn't change until the mouse button
         // is released.
@@ -1007,31 +1011,33 @@ nsSliderFrame::StartDrag(nsIDOMEvent* aE
 
   if (isHorizontal)
     mThumbStart = thumbFrame->GetPosition().x;
   else
     mThumbStart = thumbFrame->GetPosition().y;
 
   mDragStart = pos - mThumbStart;
 
-  StartAPZDrag(event);
+  mScrollingWithAPZ = StartAPZDrag(event);
 
 #ifdef DEBUG_SLIDER
   printf("Pressed mDragStart=%d\n",mDragStart);
 #endif
 
   return NS_OK;
 }
 
 nsresult
 nsSliderFrame::StopDrag()
 {
   AddListener();
   DragThumb(false);
 
+  mScrollingWithAPZ = false;
+
 #ifdef MOZ_WIDGET_GTK
   nsIFrame* thumbFrame = mFrames.FirstChild();
   if (thumbFrame) {
     nsCOMPtr<nsIContent> thumb = thumbFrame->GetContent();
     thumb->UnsetAttr(kNameSpaceID_None, nsGkAtoms::active, true);
   }
 #endif
 
--- a/layout/xul/nsSliderFrame.h
+++ b/layout/xul/nsSliderFrame.h
@@ -184,13 +184,19 @@ private:
 
   bool mDragFinished;
 
   // true if an attribute change has been caused by the user manipulating the
   // slider. This allows notifications to tell how a slider's current position
   // was changed.
   bool mUserChanged;
 
+  // true if we've handed off the scrolling to APZ. This means that we should
+  // ignore scrolling events as the position will be updated by APZ. If we were
+  // to process these events then the scroll position update would conflict
+  // causing the scroll position to jump.
+  bool mScrollingWithAPZ;
+
   static bool gMiddlePref;
   static int32_t gSnapMultiplier;
 }; // class nsSliderFrame
 
 #endif
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -4518,17 +4518,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
   }
   geckoEvent.clickCount = clickCount;
 
   if (modifierFlags & NSControlKeyMask)
     geckoEvent.button = WidgetMouseEvent::eRightButton;
   else
     geckoEvent.button = WidgetMouseEvent::eLeftButton;
 
-  mGeckoChild->DispatchInputEvent(&geckoEvent);
+  mGeckoChild->DispatchAPZAwareEvent(&geckoEvent);
   mBlockedLastMouseDown = NO;
 
   // XXX maybe call markedTextSelectionChanged:client: here?
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)mouseUp:(NSEvent *)theEvent
@@ -4545,17 +4545,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
   if ([theEvent modifierFlags] & NSControlKeyMask)
     geckoEvent.button = WidgetMouseEvent::eRightButton;
   else
     geckoEvent.button = WidgetMouseEvent::eLeftButton;
 
   // This might destroy our widget (and null out mGeckoChild).
   bool defaultPrevented =
-    (mGeckoChild->DispatchInputEvent(&geckoEvent) == nsEventStatus_eConsumeNoDefault);
+    (mGeckoChild->DispatchAPZAwareEvent(&geckoEvent) == nsEventStatus_eConsumeNoDefault);
 
   // Check to see if we are double-clicking in the titlebar.
   CGFloat locationInTitlebar = [[self window] frame].size.height - [theEvent locationInWindow].y;
   LayoutDeviceIntPoint pos = geckoEvent.refPoint;
   if (!defaultPrevented && [theEvent clickCount] == 2 &&
       mGeckoChild->GetDraggableRegion().Contains(pos.x, pos.y) &&
       [[self window] isKindOfClass:[ToolbarWindow class]] &&
       (locationInTitlebar < [(ToolbarWindow*)[self window] titlebarHeight] ||
@@ -4671,17 +4671,17 @@ NewCGSRegionFromRegion(const nsIntRegion
 
   if (!mGeckoChild)
     return;
 
   WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild,
                               WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
 
-  mGeckoChild->DispatchInputEvent(&geckoEvent);
+  mGeckoChild->DispatchAPZAwareEvent(&geckoEvent);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)mouseDragged:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
@@ -4717,17 +4717,17 @@ NewCGSRegionFromRegion(const nsIntRegion
 
   // The right mouse went down, fire off a right mouse down event to gecko
   WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild,
                               WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
   geckoEvent.button = WidgetMouseEvent::eRightButton;
   geckoEvent.clickCount = [theEvent clickCount];
 
-  mGeckoChild->DispatchInputEvent(&geckoEvent);
+  mGeckoChild->DispatchAPZAwareEvent(&geckoEvent);
   if (!mGeckoChild)
     return;
 
   // Let the superclass do the context menu stuff.
   [super rightMouseDown:theEvent];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
@@ -4741,17 +4741,17 @@ NewCGSRegionFromRegion(const nsIntRegion
 
   WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild,
                               WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
   geckoEvent.button = WidgetMouseEvent::eRightButton;
   geckoEvent.clickCount = [theEvent clickCount];
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
-  mGeckoChild->DispatchInputEvent(&geckoEvent);
+  mGeckoChild->DispatchAPZAwareEvent(&geckoEvent);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)rightMouseDragged:(NSEvent*)theEvent
 {
   if (!mGeckoChild)
     return;
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -2677,17 +2677,17 @@ nsWindow::DispatchMissedButtonReleases(G
 
             // Dispatch a synthesized button up event to tell Gecko about the
             // change in state.  This event is marked as synthesized so that
             // it is not dispatched as a DOM event, because we don't know the
             // position, widget, modifiers, or time/order.
             WidgetMouseEvent synthEvent(true, eMouseUp, this,
                                         WidgetMouseEvent::eSynthesized);
             synthEvent.button = buttonType;
-            DispatchInputEvent(&synthEvent);
+            DispatchAPZAwareEvent(&synthEvent);
         }
     }
 }
 
 void
 nsWindow::InitButtonEvent(WidgetMouseEvent& aEvent,
                           GdkEventButton* aGdkEvent)
 {
@@ -2797,17 +2797,17 @@ nsWindow::OnButtonPressEvent(GdkEventBut
 
     gButtonState |= ButtonMaskFromGDKButton(aEvent->button);
 
     WidgetMouseEvent event(true, eMouseDown, this, WidgetMouseEvent::eReal);
     event.button = domButton;
     InitButtonEvent(event, aEvent);
     event.pressure = mLastMotionPressure;
 
-    DispatchInputEvent(&event);
+    DispatchAPZAwareEvent(&event);
 
     // right menu click on linux should also pop up a context menu
     if (domButton == WidgetMouseEvent::eRightButton &&
         MOZ_LIKELY(!mIsDestroyed)) {
         WidgetMouseEvent contextMenuEvent(true, eContextMenu, this,
                                           WidgetMouseEvent::eReal);
         InitButtonEvent(contextMenuEvent, aEvent);
         contextMenuEvent.pressure = mLastMotionPressure;
@@ -2840,17 +2840,17 @@ nsWindow::OnButtonReleaseEvent(GdkEventB
     WidgetMouseEvent event(true, eMouseUp, this,
                            WidgetMouseEvent::eReal);
     event.button = domButton;
     InitButtonEvent(event, aEvent);
     gdouble pressure = 0;
     gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
     event.pressure = pressure ? pressure : mLastMotionPressure;
 
-    DispatchInputEvent(&event);
+    DispatchAPZAwareEvent(&event);
     mLastMotionPressure = pressure;
 }
 
 void
 nsWindow::OnContainerFocusInEvent(GdkEventFocus *aEvent)
 {
     LOGFOCUS(("OnContainerFocusInEvent [%p]\n", (void *)this));
 
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1011,16 +1011,18 @@ nsBaseWidget::ProcessUntransformedAPZEve
       if (wheelEvent->mFlags.mHandledByAPZ) {
         APZCCallbackHelper::SendSetTargetAPZCNotification(this, GetDocument(), *aEvent,
                   aGuid, aInputBlockId);
         if (wheelEvent->mCanTriggerSwipe) {
           ReportSwipeStarted(aInputBlockId, wheelEvent->TriggersSwipe());
         }
         mAPZEventState->ProcessWheelEvent(*wheelEvent, aGuid, aInputBlockId);
       }
+    } else if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
+      mAPZEventState->ProcessMouseEvent(*mouseEvent, aGuid, aInputBlockId);
     }
   }
 
   return status;
 }
 
 nsEventStatus
 nsBaseWidget::DispatchInputEvent(WidgetInputEvent* aEvent)
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -4223,17 +4223,17 @@ nsWindow::DispatchMouseEvent(EventMessag
         }
       }
     } else if (aEventMessage == eMouseExitFromWidget) {
       if (sCurrentWindow == this) {
         sCurrentWindow = nullptr;
       }
     }
 
-    result = ConvertStatus(DispatchInputEvent(&event));
+    result = ConvertStatus(DispatchAPZAwareEvent(&event));
 
     // Release the widget with NS_IF_RELEASE() just in case
     // the context menu key code in EventListenerManager::HandleEvent()
     // released it already.
     return result;
   }
 
   return result;