Bug 1251346 - Fennec should not generate touch events from mouse events. r=kats
authorRandall Barker <rbarker@mozilla.com>
Fri, 11 Mar 2016 15:29:50 -0800
changeset 288666 64aa2a8a7e73f5d7e49d1ab355b6ee6d38a2e627
parent 288665 7c53fd9ec1fef76b87533ac4158fd4eca7eef049
child 288667 c37cf1c267357c4df8e5f70af39a8d544a957f85
push id18174
push usercbook@mozilla.com
push dateTue, 15 Mar 2016 09:44:58 +0000
treeherderfx-team@dd0baa33759d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1251346
milestone48.0a1
Bug 1251346 - Fennec should not generate touch events from mouse events. r=kats
mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
widget/InputData.cpp
widget/InputData.h
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/nsWindow.cpp
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
@@ -372,17 +372,18 @@ public class DynamicToolbarAnimator {
         // Animations should never co-exist with the user touching the screen.
         if (mAnimationTask != null) {
             mTarget.getView().removeRenderTask(mAnimationTask);
             mAnimationTask = null;
         }
 
         // we only care about single-finger drags here; any other kind of event
         // should reset and cause us to start over.
-        if (event.getActionMasked() != MotionEvent.ACTION_MOVE ||
+        if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE ||
+            event.getActionMasked() != MotionEvent.ACTION_MOVE ||
             event.getPointerCount() != 1)
         {
             if (mTouchStart != null) {
                 Log.v(LOGTAG, "Resetting touch sequence due to non-move");
                 mTouchStart = null;
             }
 
             if (event.getActionMasked() == MotionEvent.ACTION_UP) {
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
@@ -41,19 +41,19 @@ class NativePanZoomController extends JN
 
     @WrapForJNI
     private native boolean handleScrollEvent(
             long time, int metaState,
             float x, float y,
             float hScroll, float vScroll);
 
     @WrapForJNI
-    private native boolean handleHoverEvent(
+    private native boolean handleMouseEvent(
             int action, long time, int metaState,
-            float x, float y);
+            float x, float y, int buttons);
 
     private boolean handleMotionEvent(MotionEvent event, boolean keepInViewCoordinates) {
         if (mDestroyed) {
             return false;
         }
 
         final int action = event.getActionMasked();
         final int count = event.getPointerCount();
@@ -122,33 +122,33 @@ class NativePanZoomController extends JN
 
         final float flipFactor = mNegateWheelScroll ? -1.0f : 1.0f;
         final float hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL) * flipFactor * mPointerScrollFactor;
         final float vScroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL) * flipFactor * mPointerScrollFactor;
 
         return handleScrollEvent(event.getEventTime(), event.getMetaState(), x, y, hScroll, vScroll);
     }
 
-    private boolean handleHoverEvent(MotionEvent event) {
+    private boolean handleMouseEvent(MotionEvent event) {
         if (mDestroyed) {
             return false;
         }
 
         final int count = event.getPointerCount();
 
         if (count <= 0) {
             return false;
         }
 
         final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
         event.getPointerCoords(0, coords);
         final float x = coords.x;
         final float y = coords.y;
 
-        return handleHoverEvent(event.getActionMasked(), event.getEventTime(), event.getMetaState(), x, y);
+        return handleMouseEvent(event.getActionMasked(), event.getEventTime(), event.getMetaState(), x, y, event.getButtonState());
     }
 
 
     NativePanZoomController(PanZoomTarget target, View view) {
         mTarget = target;
         mView = (LayerView) view;
 
         String[] prefs = { "ui.scrolling.negate_wheel_scroll" };
@@ -166,29 +166,33 @@ class NativePanZoomController extends JN
             mPointerScrollFactor = outValue.getDimension(view.getContext().getResources().getDisplayMetrics());
         } else {
             mPointerScrollFactor = MAX_SCROLL;
         }
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        return handleMotionEvent(event, /* keepInViewCoordinates */ true);
+        if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+            return handleMouseEvent(event);
+        } else {
+            return handleMotionEvent(event, /* keepInViewCoordinates */ true);
+        }
     }
 
     @Override
     public boolean onMotionEvent(MotionEvent event) {
         final int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_SCROLL && event.getDownTime() >= mLastDownTime) {
             mLastDownTime = event.getDownTime();
             return handleScrollEvent(event);
         } else if ((action == MotionEvent.ACTION_HOVER_MOVE) ||
                    (action == MotionEvent.ACTION_HOVER_ENTER) ||
                    (action == MotionEvent.ACTION_HOVER_EXIT)) {
-            return handleHoverEvent(event);
+            return handleMouseEvent(event);
         } else {
             return false;
         }
     }
 
     @Override
     public boolean onKeyEvent(KeyEvent event) {
         // FIXME implement this
--- a/widget/InputData.cpp
+++ b/widget/InputData.cpp
@@ -98,25 +98,28 @@ MouseInput::TransformToLocal(const Scree
 
 WidgetMouseEvent
 MouseInput::ToWidgetMouseEvent(nsIWidget* aWidget) const
 {
   MOZ_ASSERT(NS_IsMainThread(),
              "Can only convert To WidgetTouchEvent on main thread");
 
   EventMessage msg = eVoidEvent;
+  uint32_t clickCount = 0;
   switch (mType) {
     case MOUSE_MOVE:
       msg = eMouseMove;
       break;
     case MOUSE_UP:
       msg = eMouseUp;
+      clickCount = 1;
       break;
     case MOUSE_DOWN:
       msg = eMouseDown;
+      clickCount = 1;
       break;
     case MOUSE_DRAG_START:
       msg = eDragStart;
       break;
     case MOUSE_DRAG_END:
       msg = eDragEnd;
       break;
     case MOUSE_WIDGET_ENTER:
@@ -153,16 +156,19 @@ MouseInput::ToWidgetMouseEvent(nsIWidget
 
   event.buttons = mButtons;
   event.modifiers = modifiers;
   event.time = mTime;
   event.timeStamp = mTimeStamp;
   event.refPoint =
     RoundedToInt(ViewAs<LayoutDevicePixel>(mOrigin,
       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
+  event.clickCount = clickCount;
+  event.inputSource = mInputSource;
+  event.ignoreRootScrollFrame = true;
 
   return event;
 }
 
 MultiTouchInput::MultiTouchInput(const WidgetTouchEvent& aTouchEvent)
   : InputData(MULTITOUCH_INPUT, aTouchEvent.time, aTouchEvent.timeStamp,
               aTouchEvent.modifiers)
   , mHandledByAPZ(aTouchEvent.mFlags.mHandledByAPZ)
--- a/widget/InputData.h
+++ b/widget/InputData.h
@@ -267,41 +267,44 @@ public:
   enum ButtonType
   {
     LEFT_BUTTON,
     MIDDLE_BUTTON,
     RIGHT_BUTTON,
     NONE
   };
 
-  MouseInput(MouseType aType, ButtonType aButtonType, int16_t aButtons, const ScreenPoint& aPoint,
+  MouseInput(MouseType aType, ButtonType aButtonType, uint16_t aInputSource, int16_t aButtons, const ScreenPoint& aPoint,
              uint32_t aTime, TimeStamp aTimeStamp, Modifiers aModifiers)
     : InputData(MOUSE_INPUT, aTime, aTimeStamp, aModifiers)
     , mType(aType)
     , mButtonType(aButtonType)
+    , mInputSource(aInputSource)
     , mButtons(aButtons)
     , mOrigin(aPoint)
   {}
 
   MouseInput()
     : InputData(MOUSE_INPUT)
     , mType(MOUSE_NONE)
     , mButtonType(NONE)
+    , mInputSource(0)
     , mButtons(0)
   {}
 
   explicit MouseInput(const WidgetMouseEventBase& aMouseEvent);
 
   bool IsLeftButton() const { return mButtonType == LEFT_BUTTON; }
 
   bool TransformToLocal(const ScreenToParentLayerMatrix4x4& aTransform);
   WidgetMouseEvent ToWidgetMouseEvent(nsIWidget* aWidget) const;
 
   MouseType mType;
   ButtonType mButtonType;
+  uint16_t mInputSource;
   int16_t mButtons;
   ScreenPoint mOrigin;
   ParentLayerPoint mLocalOrigin;
 };
 
 /**
  * Encapsulation class for pan events, can be used off-main-thread.
  * These events are currently only used for scrolling on desktop.
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -289,24 +289,24 @@ public:
         mozilla::jni::MakeNativeMethod<NativePanZoomController::AdjustScrollForSurfaceShift_t>(
                 mozilla::jni::NativeStub<NativePanZoomController::AdjustScrollForSurfaceShift_t, Impl>
                 ::template Wrap<&Impl::AdjustScrollForSurfaceShift>),
 
         mozilla::jni::MakeNativeMethod<NativePanZoomController::DisposeNative_t>(
                 mozilla::jni::NativeStub<NativePanZoomController::DisposeNative_t, Impl>
                 ::template Wrap<&Impl::DisposeNative>),
 
-        mozilla::jni::MakeNativeMethod<NativePanZoomController::HandleHoverEvent_t>(
-                mozilla::jni::NativeStub<NativePanZoomController::HandleHoverEvent_t, Impl>
-                ::template Wrap<&Impl::HandleHoverEvent>),
-
         mozilla::jni::MakeNativeMethod<NativePanZoomController::HandleMotionEvent_t>(
                 mozilla::jni::NativeStub<NativePanZoomController::HandleMotionEvent_t, Impl>
                 ::template Wrap<&Impl::HandleMotionEvent>),
 
+        mozilla::jni::MakeNativeMethod<NativePanZoomController::HandleMouseEvent_t>(
+                mozilla::jni::NativeStub<NativePanZoomController::HandleMouseEvent_t, Impl>
+                ::template Wrap<&Impl::HandleMouseEvent>),
+
         mozilla::jni::MakeNativeMethod<NativePanZoomController::HandleScrollEvent_t>(
                 mozilla::jni::NativeStub<NativePanZoomController::HandleScrollEvent_t, Impl>
                 ::template Wrap<&Impl::HandleScrollEvent>),
 
         mozilla::jni::MakeNativeMethod<NativePanZoomController::AbortAnimation_t>(
                 mozilla::jni::NativeStub<NativePanZoomController::AbortAnimation_t, Impl>
                 ::template Wrap<&Impl::AbortAnimation>),
 
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -1533,22 +1533,22 @@ constexpr char NativePanZoomController::
 auto NativePanZoomController::Destroy() const -> void
 {
     return mozilla::jni::Method<Destroy_t>::Call(NativePanZoomController::mCtx, nullptr);
 }
 
 constexpr char NativePanZoomController::DisposeNative_t::name[];
 constexpr char NativePanZoomController::DisposeNative_t::signature[];
 
-constexpr char NativePanZoomController::HandleHoverEvent_t::name[];
-constexpr char NativePanZoomController::HandleHoverEvent_t::signature[];
-
 constexpr char NativePanZoomController::HandleMotionEvent_t::name[];
 constexpr char NativePanZoomController::HandleMotionEvent_t::signature[];
 
+constexpr char NativePanZoomController::HandleMouseEvent_t::name[];
+constexpr char NativePanZoomController::HandleMouseEvent_t::signature[];
+
 constexpr char NativePanZoomController::HandleScrollEvent_t::name[];
 constexpr char NativePanZoomController::HandleScrollEvent_t::signature[];
 
 constexpr char NativePanZoomController::AbortAnimation_t::name[];
 constexpr char NativePanZoomController::AbortAnimation_t::signature[];
 
 constexpr char NativePanZoomController::SetIsLongpressEnabled_t::name[];
 constexpr char NativePanZoomController::SetIsLongpressEnabled_t::signature[];
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -3620,34 +3620,16 @@ public:
         static constexpr char name[] = "disposeNative";
         static constexpr char signature[] =
                 "()V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
-    struct HandleHoverEvent_t {
-        typedef NativePanZoomController Owner;
-        typedef bool ReturnType;
-        typedef bool SetterType;
-        typedef mozilla::jni::Args<
-                int32_t,
-                int64_t,
-                int32_t,
-                float,
-                float> Args;
-        static constexpr char name[] = "handleHoverEvent";
-        static constexpr char signature[] =
-                "(IJIFF)Z";
-        static const bool isStatic = false;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-    };
-
     struct HandleMotionEvent_t {
         typedef NativePanZoomController Owner;
         typedef bool ReturnType;
         typedef bool SetterType;
         typedef mozilla::jni::Args<
                 int32_t,
                 int32_t,
                 int64_t,
@@ -3662,16 +3644,35 @@ public:
         static constexpr char name[] = "handleMotionEvent";
         static constexpr char signature[] =
                 "(IIJI[I[F[F[F[F[F[F)Z";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
+    struct HandleMouseEvent_t {
+        typedef NativePanZoomController Owner;
+        typedef bool ReturnType;
+        typedef bool SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int64_t,
+                int32_t,
+                float,
+                float,
+                int32_t> Args;
+        static constexpr char name[] = "handleMouseEvent";
+        static constexpr char signature[] =
+                "(IJIFFI)Z";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
     struct HandleScrollEvent_t {
         typedef NativePanZoomController Owner;
         typedef bool ReturnType;
         typedef bool SetterType;
         typedef mozilla::jni::Args<
                 int64_t,
                 int32_t,
                 float,
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -378,25 +378,27 @@ public:
  */
 class nsWindow::NPZCSupport final
     : public NativePanZoomController::Natives<NPZCSupport>
 {
     nsWindow* mWindow;
     // Lock for keeping mWindow alive when accessed off of the Gecko thread.
     Mutex mWindowLock;
     NativePanZoomController::GlobalRef mNPZC;
+    int mPreviousButtons;
 
 public:
     typedef NativePanZoomController::Natives<NPZCSupport> Base;
 
     NPZCSupport(nsWindow* aWindow,
                 const NativePanZoomController::LocalRef& aNPZC)
         : mWindow(aWindow)
         , mWindowLock("NPZCSupport")
         , mNPZC(aNPZC)
+        , mPreviousButtons(0)
     {
         if (mWindow->mNPZCSupport) {
             mWindow->mNPZCSupport->DetachFromWindow();
         }
         mWindow->mNPZCSupport = this;
     }
 
     ~NPZCSupport()
@@ -575,54 +577,111 @@ public:
             WidgetWheelEvent wheelEvent = input.ToWidgetWheelEvent(window);
             window->ProcessUntransformedAPZEvent(&wheelEvent, guid,
                                                  blockId, status);
         });
 
         return true;
     }
 
-    bool HandleHoverEvent(int32_t aAction, int64_t aTime, int32_t aMetaState,
-                          float aX, float aY)
+    static MouseInput::ButtonType GetButtonType(int button)
+    {
+        MouseInput::ButtonType result = MouseInput::NONE;
+
+        switch (button) {
+            case widget::sdk::MotionEvent::BUTTON_PRIMARY:
+                result = MouseInput::LEFT_BUTTON;
+                break;
+            case widget::sdk::MotionEvent::BUTTON_SECONDARY:
+                result = MouseInput::RIGHT_BUTTON;
+                break;
+            case widget::sdk::MotionEvent::BUTTON_TERTIARY:
+                result = MouseInput::MIDDLE_BUTTON;
+                break;
+            default:
+                break;
+        }
+
+        return result;
+    }
+
+    static int16_t ConvertButtons(int buttons) {
+        int16_t result = 0;
+
+        if (buttons & widget::sdk::MotionEvent::BUTTON_PRIMARY) {
+            result |= WidgetMouseEventBase::eLeftButtonFlag;
+        }
+        if (buttons & widget::sdk::MotionEvent::BUTTON_SECONDARY) {
+            result |= WidgetMouseEventBase::eRightButtonFlag;
+        }
+        if (buttons & widget::sdk::MotionEvent::BUTTON_TERTIARY) {
+            result |= WidgetMouseEventBase::eMiddleButtonFlag;
+        }
+        if (buttons & widget::sdk::MotionEvent::BUTTON_BACK) {
+            result |= WidgetMouseEventBase::e4thButtonFlag;
+        }
+        if (buttons & widget::sdk::MotionEvent::BUTTON_FORWARD) {
+            result |= WidgetMouseEventBase::e5thButtonFlag;
+        }
+
+        return result;
+    }
+
+    bool HandleMouseEvent(int32_t aAction, int64_t aTime, int32_t aMetaState,
+                          float aX, float aY, int buttons)
     {
         MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
 
         MutexAutoLock lock(mWindowLock);
         if (!mWindow) {
             // We already shut down.
             return false;
         }
 
         RefPtr<APZCTreeManager> controller = mWindow->mAPZC;
         if (!controller) {
             return false;
         }
 
-        MouseInput::MouseType type = MouseInput::MOUSE_NONE;
+        MouseInput::MouseType mouseType = MouseInput::MOUSE_NONE;
+        MouseInput::ButtonType buttonType = MouseInput::NONE;
         switch (aAction) {
+            case AndroidMotionEvent::ACTION_DOWN:
+                mouseType = MouseInput::MOUSE_DOWN;
+                buttonType = GetButtonType(buttons ^ mPreviousButtons);
+                mPreviousButtons = buttons;
+                break;
+            case AndroidMotionEvent::ACTION_UP:
+                mouseType = MouseInput::MOUSE_UP;
+                buttonType = GetButtonType(buttons ^ mPreviousButtons);
+                mPreviousButtons = buttons;
+                break;
+            case AndroidMotionEvent::ACTION_MOVE:
+                mouseType = MouseInput::MOUSE_MOVE;
+                break;
             case AndroidMotionEvent::ACTION_HOVER_MOVE:
-                type = MouseInput::MOUSE_MOVE;
+                mouseType = MouseInput::MOUSE_MOVE;
                 break;
             case AndroidMotionEvent::ACTION_HOVER_ENTER:
-                type = MouseInput::MOUSE_WIDGET_ENTER;
+                mouseType = MouseInput::MOUSE_WIDGET_ENTER;
                 break;
             case AndroidMotionEvent::ACTION_HOVER_EXIT:
-                type = MouseInput::MOUSE_WIDGET_EXIT;
+                mouseType = MouseInput::MOUSE_WIDGET_EXIT;
                 break;
             default:
                 break;
         }
 
-        if (type == MouseInput::MOUSE_NONE) {
+        if (mouseType == MouseInput::MOUSE_NONE) {
             return false;
         }
 
         ScreenPoint origin = ScreenPoint(aX, aY);
 
-        MouseInput input(type, MouseInput::NONE, 0, origin, aTime, TimeStamp(), GetModifiers(aMetaState));
+        MouseInput input(mouseType, buttonType, nsIDOMMouseEvent::MOZ_SOURCE_MOUSE, ConvertButtons(buttons), origin, aTime, TimeStamp(), GetModifiers(aMetaState));
 
         ScrollableLayerGuid guid;
         uint64_t blockId;
         nsEventStatus status = controller->ReceiveInputEvent(input, &guid, &blockId);
 
         if (status == nsEventStatus_eConsumeNoDefault) {
             return true;
         }