Bug 1260018 - Route drag events to APZ, so it can accurately detect the end of a drag. r=kats
authorBotond Ballo <botond@mozilla.com>
Mon, 18 Apr 2016 14:24:28 -0400
changeset 331923 746c21b668ac307976d6fb142f4b708146b58ee7
parent 331922 f7caa27d445de33f90599f4b3bb5166255e17a97
child 331924 ab5be7357e5372c76c3510300de99a89dfbc1020
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1260018
milestone48.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 1260018 - Route drag events to APZ, so it can accurately detect the end of a drag. r=kats
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/DragTracker.cpp
widget/nsBaseDragService.cpp
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -618,17 +618,18 @@ WillHandleWheelEvent(WidgetWheelEvent* a
           aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE);
 }
 
 static bool
 WillHandleMouseEvent(const WidgetMouseEventBase& aEvent)
 {
   return aEvent.mMessage == eMouseMove ||
          aEvent.mMessage == eMouseDown ||
-         aEvent.mMessage == eMouseUp;
+         aEvent.mMessage == eMouseUp ||
+         aEvent.mMessage == eDragEnd;
 }
 
 template<typename PanGestureOrScrollWheelInput>
 static bool
 WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput)
 {
   if (!NS_IsMainThread()) {
     return true;
@@ -1145,17 +1146,18 @@ APZCTreeManager::ReceiveInputEvent(Widge
 
   // Initialize aOutInputBlockId to a sane value, and then later we overwrite
   // it if the input event goes into a block.
   if (aOutInputBlockId) {
     *aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
   }
 
   switch (aEvent.mClass) {
-    case eMouseEventClass: {
+    case eMouseEventClass:
+    case eDragEventClass: {
       WidgetMouseEventBase& mouseEvent = *aEvent.AsMouseEventBase();
       if (WillHandleMouseEvent(mouseEvent)) {
         return ProcessMouseEvent(mouseEvent, aOutTargetGuid, aOutInputBlockId);
       }
       return ProcessEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
     }
     case eTouchEventClass: {
       WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
--- a/gfx/layers/apz/src/DragTracker.cpp
+++ b/gfx/layers/apz/src/DragTracker.cpp
@@ -22,17 +22,24 @@ DragTracker::DragTracker()
 DragTracker::StartsDrag(const MouseInput& aInput)
 {
   return aInput.IsLeftButton() && aInput.mType == MouseInput::MOUSE_DOWN;
 }
 
 /*static*/ bool
 DragTracker::EndsDrag(const MouseInput& aInput)
 {
-  return aInput.IsLeftButton() && aInput.mType == MouseInput::MOUSE_UP;
+  // On Windows, we don't receive a MOUSE_UP at the end of a drag if an
+  // actual drag session took place. As a backup, we detect the end of the
+  // drag using the MOUSE_DRAG_END event, which normally is routed directly
+  // to content, but we're specially routing to APZ for this purpose. Bug
+  // 1265105 tracks a solution to this at the Windows widget layer; once
+  // that is implemented, this workaround can be removed.
+  return (aInput.IsLeftButton() && aInput.mType == MouseInput::MOUSE_UP)
+      || aInput.mType == MouseInput::MOUSE_DRAG_END;
 }
 
 void
 DragTracker::Update(const MouseInput& aInput)
 {
   if (StartsDrag(aInput)) {
     DRAG_LOG("Starting drag\n");
     mInDrag = true;
--- a/widget/nsBaseDragService.cpp
+++ b/widget/nsBaseDragService.cpp
@@ -432,16 +432,24 @@ nsBaseDragService::FireDragEventAtSource
         nsEventStatus status = nsEventStatus_eIgnore;
         WidgetDragEvent event(true, aEventMessage, nullptr);
         event.inputSource = mInputSource;
         if (aEventMessage == eDragEnd) {
           event.mRefPoint = mEndDragPoint;
           event.mUserCancelled = mUserCancelled;
         }
 
+        // Send the drag event to APZ, which needs to know about them to be
+        // able to accurately detect the end of a drag gesture.
+        if (nsPresContext* presContext = presShell->GetPresContext()) {
+          if (nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget()) {
+            widget->DispatchEventToAPZOnly(&event);
+          }
+        }
+
         nsCOMPtr<nsIContent> content = do_QueryInterface(mSourceNode);
         return presShell->HandleDOMEventWithTarget(content, &event, &status);
       }
     }
   }
 
   return NS_OK;
 }
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1192,16 +1192,28 @@ nsBaseWidget::DispatchInputEvent(WidgetI
     }
   }
 
   nsEventStatus status;
   DispatchEvent(aEvent, status);
   return status;
 }
 
+void
+nsBaseWidget::DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mAPZC) {
+    MOZ_ASSERT(APZThreadUtils::IsControllerThread());
+    uint64_t inputBlockId = 0;
+    ScrollableLayerGuid guid;
+    mAPZC->ReceiveInputEvent(*aEvent, &guid, &inputBlockId);
+  }
+}
+
 nsIDocument*
 nsBaseWidget::GetDocument() const
 {
   if (mWidgetListener) {
     if (nsIPresShell* presShell = mWidgetListener->GetPresShell()) {
       return presShell->GetDocument();
     }
   }
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -260,16 +260,17 @@ public:
   NS_IMETHOD_(TextEventDispatcherListener*)
     GetNativeTextEventDispatcherListener() override;
   virtual void ZoomToRect(const uint32_t& aPresShellId,
                           const FrameMetrics::ViewID& aViewId,
                           const CSSRect& aRect,
                           const uint32_t& aFlags) override;
   // Dispatch an event that must be first be routed through APZ.
   nsEventStatus DispatchInputEvent(mozilla::WidgetInputEvent* aEvent) override;
+  void DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent) override;
 
   void SetConfirmedTargetAPZC(uint64_t aInputBlockId,
                               const nsTArray<ScrollableLayerGuid>& aTargets) const override;
 
   void UpdateZoomConstraints(const uint32_t& aPresShellId,
                              const FrameMetrics::ViewID& aViewId,
                              const mozilla::Maybe<ZoomConstraints>& aConstraints) override;
 
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1420,16 +1420,22 @@ class nsIWidget : public nsISupports {
     /**
      * Dispatches an event to the widget
      *
      */
     NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* event,
                              nsEventStatus & aStatus) = 0;
 
     /**
+     * Dispatches an event to APZ only.
+     * No-op in the child process.
+     */
+    virtual void DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent) = 0;
+
+    /**
      * Dispatches an event that must be handled by APZ first, when APZ is
      * enabled. If invoked in the child process, it is forwarded to the
      * parent process synchronously.
      */
     virtual nsEventStatus DispatchInputEvent(mozilla::WidgetInputEvent* aEvent) = 0;
 
     /**
      * Confirm an APZ-aware event target. This should be used when APZ will