Bug 1031362 Part1: Dispatching mouse events by handling pen generated WM_POINTER* messages. r=jimm,smaug
☠☠ backed out by c59499ae612d ☠ ☠
authorStone Shih <sshih@mozilla.com>
Sun, 20 Nov 2016 10:24:46 +0800
changeset 373843 1413fa80263234c7a2cd24056d62509e2bd2e720
parent 373842 07bb0ca0c7044baaa87f217a4404435208a6db99
child 373844 bb1654cb6f301e6c9d412b5ab393872c92df63cd
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm, smaug
bugs1031362
milestone53.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 1031362 Part1: Dispatching mouse events by handling pen generated WM_POINTER* messages. r=jimm,smaug
widget/MouseEvents.h
widget/windows/WinPointerEvents.cpp
widget/windows/WinPointerEvents.h
widget/windows/moz.build
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
--- a/widget/MouseEvents.h
+++ b/widget/MouseEvents.h
@@ -53,16 +53,25 @@ public:
     : pointerId(0)
     , tiltX(0)
     , tiltY(0)
     , convertToPointer(true)
     , retargetedByPointerCapture(false)
   {
   }
 
+  WidgetPointerHelper(uint32_t aPointerId, uint32_t aTiltX, uint32_t aTiltY)
+    : pointerId(aPointerId)
+    , tiltX(aTiltX)
+    , tiltY(aTiltY)
+    , convertToPointer(true)
+    , retargetedByPointerCapture(false)
+  {
+  }
+
   void AssignPointerHelperData(const WidgetPointerHelper& aEvent)
   {
     pointerId = aEvent.pointerId;
     tiltX = aEvent.tiltX;
     tiltY = aEvent.tiltY;
     convertToPointer = aEvent.convertToPointer;
     retargetedByPointerCapture = aEvent.retargetedByPointerCapture;
   }
new file mode 100644
--- /dev/null
+++ b/widget/windows/WinPointerEvents.cpp
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+/*
+ * WinPointerEvents - Helper functions to retrieve PointerEvent's attributes
+ */
+
+#include "nscore.h"
+#include "WinPointerEvents.h"
+#include "mozilla/MouseEvents.h"
+
+using namespace mozilla;
+using namespace mozilla::widget;
+
+const wchar_t WinPointerEvents::kPointerLibraryName[] =  L"user32.dll";
+HMODULE WinPointerEvents::sLibraryHandle = nullptr;
+WinPointerEvents::GetPointerTypePtr WinPointerEvents::getPointerType = nullptr;
+WinPointerEvents::GetPointerInfoPtr WinPointerEvents::getPointerInfo = nullptr;
+WinPointerEvents::GetPointerPenInfoPtr WinPointerEvents::getPointerPenInfo = nullptr;
+bool WinPointerEvents::sPointerEventEnabled = true;
+
+WinPointerEvents::WinPointerEvents()
+{
+  InitLibrary();
+  static bool addedPointerEventEnabled = false;
+  if (!addedPointerEventEnabled) {
+    Preferences::AddBoolVarCache(&sPointerEventEnabled,
+                                 "dom.w3c_pointer_events.enabled", true);
+    addedPointerEventEnabled = true;
+  }
+}
+
+/* Load and shutdown */
+void
+WinPointerEvents::InitLibrary()
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  if (!IsWin8OrLater()) {
+    // Only Win8 or later supports WM_POINTER*
+    return;
+  }
+  if (getPointerType) {
+    // Return if we already initialized the PointerEvent related interfaces
+    return;
+  }
+  sLibraryHandle = ::LoadLibraryW(kPointerLibraryName);
+  MOZ_ASSERT(sLibraryHandle, "cannot load pointer library");
+  if (sLibraryHandle) {
+    getPointerType =
+      (GetPointerTypePtr)GetProcAddress(sLibraryHandle, "GetPointerType");
+    getPointerInfo =
+      (GetPointerInfoPtr)GetProcAddress(sLibraryHandle, "GetPointerInfo");
+    getPointerPenInfo =
+      (GetPointerPenInfoPtr)GetProcAddress(sLibraryHandle, "GetPointerPenInfo");
+  }
+
+  if (!getPointerType || !getPointerInfo || !getPointerPenInfo) {
+    MOZ_ASSERT(false, "get PointerEvent interfaces failed");
+    getPointerType = nullptr;
+    getPointerInfo = nullptr;
+    getPointerPenInfo = nullptr;
+    return;
+  }
+}
+
+bool
+WinPointerEvents::ShouldFireCompatibilityMouseEventsForPen(WPARAM aWParam)
+{
+  if (!sLibraryHandle || !sPointerEventEnabled) {
+    // Firing mouse events by handling Windows WM_POINTER* when preference is on
+    // and the Windows platform supports PointerEvent related interfaces.
+    return false;
+  }
+
+  uint32_t pointerId = GetPointerId(aWParam);
+  POINTER_INPUT_TYPE pointerType = PT_POINTER;
+  if (!GetPointerType(pointerId, &pointerType)) {
+    MOZ_ASSERT(false, "cannot find PointerType");
+    return false;
+  }
+  return (pointerType == PT_PEN);
+}
+
+bool
+WinPointerEvents::GetPointerType(uint32_t aPointerId,
+                                 POINTER_INPUT_TYPE *aPointerType)
+{
+  if (!getPointerType) {
+    return false;
+  }
+  return getPointerType(aPointerId, aPointerType);
+}
+
+bool
+WinPointerEvents::GetPointerInfo(uint32_t aPointerId,
+                                 POINTER_INFO *aPointerInfo)
+{
+  if (!getPointerInfo) {
+    return false;
+  }
+  return getPointerInfo(aPointerId, aPointerInfo);
+}
+
+bool
+WinPointerEvents::GetPointerPenInfo(uint32_t aPointerId,
+                                    POINTER_PEN_INFO *aPenInfo)
+{
+  if (!getPointerPenInfo) {
+    return false;
+  }
+  return getPointerPenInfo(aPointerId, aPenInfo);
+}
new file mode 100644
--- /dev/null
+++ b/widget/windows/WinPointerEvents.h
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 2; 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 WinPointerEvents_h__
+#define WinPointerEvents_h__
+
+#include "mozilla/MouseEvents.h"
+
+// Define PointerEvent related macros and structures when building code on
+// Windows version before Win8.
+#if WINVER < 0x0602
+
+// These definitions are copied from WinUser.h. Some of them are not used but
+// keep them here for future usage.
+#define WM_NCPOINTERUPDATE              0x0241
+#define WM_NCPOINTERDOWN                0x0242
+#define WM_NCPOINTERUP                  0x0243
+#define WM_POINTERUPDATE                0x0245
+#define WM_POINTERDOWN                  0x0246
+#define WM_POINTERUP                    0x0247
+#define WM_POINTERENTER                 0x0249
+#define WM_POINTERLEAVE                 0x024A
+#define WM_POINTERACTIVATE              0x024B
+#define WM_POINTERCAPTURECHANGED        0x024C
+#define WM_TOUCHHITTESTING              0x024D
+#define WM_POINTERWHEEL                 0x024E
+#define WM_POINTERHWHEEL                0x024F
+#define DM_POINTERHITTEST               0x0250
+
+typedef UINT32 PEN_FLAGS;
+#define PEN_FLAG_NONE                   0x00000000 // Default
+#define PEN_FLAG_BARREL                 0x00000001 // The barrel button is pressed
+#define PEN_FLAG_INVERTED               0x00000002 // The pen is inverted
+#define PEN_FLAG_ERASER                 0x00000004 // The eraser button is pressed
+
+typedef UINT32 PEN_MASK;
+#define PEN_MASK_NONE                   0x00000000 // Default - none of the optional fields are valid
+#define PEN_MASK_PRESSURE               0x00000001 // The pressure field is valid
+#define PEN_MASK_ROTATION               0x00000002 // The rotation field is valid
+#define PEN_MASK_TILT_X                 0x00000004 // The tiltX field is valid
+#define PEN_MASK_TILT_Y                 0x00000008 // The tiltY field is valid
+
+typedef struct tagPOINTER_PEN_INFO {
+    POINTER_INFO    pointerInfo;
+    PEN_FLAGS       penFlags;
+    PEN_MASK        penMask;
+    UINT32          pressure;
+    UINT32          rotation;
+    INT32           tiltX;
+    INT32           tiltY;
+} POINTER_PEN_INFO;
+
+/*
+ * Flags that appear in pointer input message parameters
+ */
+#define POINTER_MESSAGE_FLAG_NEW                0x00000001 // New pointer
+#define POINTER_MESSAGE_FLAG_INRANGE            0x00000002 // Pointer has not departed
+#define POINTER_MESSAGE_FLAG_INCONTACT          0x00000004 // Pointer is in contact
+#define POINTER_MESSAGE_FLAG_FIRSTBUTTON        0x00000010 // Primary action
+#define POINTER_MESSAGE_FLAG_SECONDBUTTON       0x00000020 // Secondary action
+#define POINTER_MESSAGE_FLAG_THIRDBUTTON        0x00000040 // Third button
+#define POINTER_MESSAGE_FLAG_FOURTHBUTTON       0x00000080 // Fourth button
+#define POINTER_MESSAGE_FLAG_FIFTHBUTTON        0x00000100 // Fifth button
+#define POINTER_MESSAGE_FLAG_PRIMARY            0x00002000 // Pointer is primary
+#define POINTER_MESSAGE_FLAG_CONFIDENCE         0x00004000 // Pointer is considered unlikely to be accidental
+#define POINTER_MESSAGE_FLAG_CANCELED           0x00008000 // Pointer is departing in an abnormal manner
+
+/*
+ * Macros to retrieve information from pointer input message parameters
+ */
+#define GET_POINTERID_WPARAM(wParam)                (LOWORD(wParam))
+#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag)    (((DWORD)HIWORD(wParam) & (flag)) == (flag))
+#define IS_POINTER_NEW_WPARAM(wParam)               IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_NEW)
+#define IS_POINTER_INRANGE_WPARAM(wParam)           IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INRANGE)
+#define IS_POINTER_INCONTACT_WPARAM(wParam)         IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INCONTACT)
+#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam)       IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON)
+#define IS_POINTER_SECONDBUTTON_WPARAM(wParam)      IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON)
+#define IS_POINTER_THIRDBUTTON_WPARAM(wParam)       IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON)
+#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam)      IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON)
+#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam)       IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON)
+#define IS_POINTER_PRIMARY_WPARAM(wParam)           IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_PRIMARY)
+#define HAS_POINTER_CONFIDENCE_WPARAM(wParam)       IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CONFIDENCE)
+#define IS_POINTER_CANCELED_WPARAM(wParam)          IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED)
+
+/*
+ * WM_POINTERACTIVATE return codes
+ */
+#define PA_ACTIVATE                     MA_ACTIVATE
+#define PA_NOACTIVATE                   MA_NOACTIVATE
+
+#endif // WINVER < 0x0602
+
+/******************************************************************************
+ * WinPointerInfo
+ *
+ * This is a helper class to handle WM_POINTER*. It only supports Win8 or later.
+ *
+ ******************************************************************************/
+class WinPointerInfo final : public mozilla::WidgetPointerHelper
+{
+public:
+  WinPointerInfo()
+    : WidgetPointerHelper()
+  {
+  }
+
+  WinPointerInfo(uint32_t aPointerId, uint32_t aTiltX, uint32_t aTiltY,
+                 float aPressure, int16_t aButtons)
+    : WidgetPointerHelper(aPointerId, aTiltX, aTiltY)
+    , mPressure(aPressure)
+    , mButtons(aButtons)
+  {
+  }
+
+  float mPressure;
+  int16_t mButtons;
+};
+
+class WinPointerEvents final
+{
+public:
+  explicit WinPointerEvents();
+
+public:
+  bool ShouldFireCompatibilityMouseEventsForPen(WPARAM aWParam);
+
+  uint32_t GetPointerId(WPARAM aWParam)
+  {
+    return GET_POINTERID_WPARAM(aWParam);
+  }
+  bool GetPointerType(uint32_t aPointerId, POINTER_INPUT_TYPE *aPointerType);
+  bool GetPointerInfo(uint32_t aPointerId, POINTER_INFO *aPointerInfo);
+  bool GetPointerPenInfo(uint32_t aPointerId, POINTER_PEN_INFO *aPenInfo);
+
+private:
+  // Function prototypes
+  typedef BOOL (WINAPI* GetPointerTypePtr)(uint32_t aPointerId,
+                                           POINTER_INPUT_TYPE *aPointerType);
+  typedef BOOL (WINAPI* GetPointerInfoPtr)(uint32_t aPointerId,
+                                           POINTER_INFO *aPointerInfo);
+  typedef BOOL (WINAPI* GetPointerPenInfoPtr)(uint32_t aPointerId,
+                                              POINTER_PEN_INFO *aPenInfo);
+
+  void InitLibrary();
+
+  static HMODULE sLibraryHandle;
+  static const wchar_t kPointerLibraryName[];
+  static bool sPointerEventEnabled;
+  // Static function pointers
+  static GetPointerTypePtr getPointerType;
+  static GetPointerInfoPtr getPointerInfo;
+  static GetPointerPenInfoPtr getPointerPenInfo;
+};
+
+#endif // #ifndef WinPointerEvents_h__
--- a/widget/windows/moz.build
+++ b/widget/windows/moz.build
@@ -58,16 +58,17 @@ UNIFIED_SOURCES += [
     'nsWinGesture.cpp',
     'TaskbarPreview.cpp',
     'TaskbarPreviewButton.cpp',
     'TaskbarTabPreview.cpp',
     'TaskbarWindowPreview.cpp',
     'WidgetTraceEvent.cpp',
     'WindowHook.cpp',
     'WinIMEHandler.cpp',
+    'WinPointerEvents.cpp',
     'WinTaskbar.cpp',
     'WinTextEventDispatcherListener.cpp',
     'WinUtils.cpp',
 ]
 
 # The following files cannot be built in unified mode because of name clashes.
 SOURCES += [
     'JumpListBuilder.cpp',
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -3393,17 +3393,17 @@ nsWindow::MakeFullScreen(bool aFullScree
   // Send a eMouseEnterIntoWidget event since Windows has already sent
   // a WM_MOUSELEAVE that caused us to send a eMouseExitFromWidget event.
   if (aFullScreen && !sCurrentWindow) {
     sCurrentWindow = this;
     LPARAM pos = sCurrentWindow->lParamToClient(sMouseExitlParamScreen);
     sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget,
                                        sMouseExitwParam, pos, false,
                                        WidgetMouseEvent::eLeftButton,
-                                       MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                       MOUSE_INPUT_SOURCE());
   }
 
   return NS_OK;
 }
 
 /**************************************************************
  *
  * SECTION: Native data storage
@@ -4127,17 +4127,17 @@ bool nsWindow::DispatchPluginEvent(UINT 
   return ret;
 }
 
 // Deal with all sort of mouse event
 bool
 nsWindow::DispatchMouseEvent(EventMessage aEventMessage, WPARAM wParam,
                              LPARAM lParam, bool aIsContextMenuKey,
                              int16_t aButton, uint16_t aInputSource,
-                             uint16_t aPointerId)
+                             WinPointerInfo* aPointerInfo)
 {
   bool result = false;
 
   UserActivity();
 
   if (!mWidgetListener) {
     return result;
   }
@@ -4164,25 +4164,28 @@ nsWindow::DispatchMouseEvent(EventMessag
       // If mTouchWindow is true, then we must have APZ enabled and be
       // feeding it raw touch events. In that case we don't need to
       // send touch-generated mouse events to content.
       MOZ_ASSERT(mAPZC);
       return result;
     }
   }
 
+  uint32_t pointerId = aPointerInfo ? aPointerInfo->pointerId :
+                                      MOUSE_POINTERID();
+
   // Since it is unclear whether a user will use the digitizer,
   // Postpone initialization until first PEN message will be found.
   if (nsIDOMMouseEvent::MOZ_SOURCE_PEN == aInputSource
       // Messages should be only at topLevel window.
       && nsWindowType::eWindowType_toplevel == mWindowType
       // Currently this scheme is used only when pointer events is enabled.
       && gfxPrefs::PointerEventsEnabled()) {
     InkCollector::sInkCollector->SetTarget(mWnd);
-    InkCollector::sInkCollector->SetPointerId(aPointerId);
+    InkCollector::sInkCollector->SetPointerId(pointerId);
   }
 
   switch (aEventMessage) {
     case eMouseDown:
       CaptureMouse(true);
       break;
 
     // eMouseMove and eMouseExitFromWidget are here because we need to make
@@ -4209,20 +4212,28 @@ nsWindow::DispatchMouseEvent(EventMessag
   } else {
     InitEvent(event, &eventPoint);
   }
 
   ModifierKeyState modifierKeyState;
   modifierKeyState.InitInputEvent(event);
   event.button    = aButton;
   event.inputSource = aInputSource;
-  event.pointerId = aPointerId;
-  // If we get here the mouse events must be from non-touch sources, so
-  // convert it to pointer events as well
-  event.convertToPointer = true;
+  if (aPointerInfo) {
+    // Mouse events from Windows WM_POINTER*. Fill more information in
+    // WidgetMouseEvent.
+    event.AssignPointerHelperData(*aPointerInfo);
+    event.pressure = aPointerInfo->mPressure;
+    event.buttons = aPointerInfo->mButtons;
+  } else {
+    // If we get here the mouse events must be from non-touch sources, so
+    // convert it to pointer events as well
+    event.convertToPointer = true;
+    event.pointerId = pointerId;
+  }
 
   bool insideMovementThreshold = (DeprecatedAbs(sLastMousePoint.x - eventPoint.x) < (short)::GetSystemMetrics(SM_CXDOUBLECLK)) &&
                                    (DeprecatedAbs(sLastMousePoint.y - eventPoint.y) < (short)::GetSystemMetrics(SM_CYDOUBLECLK));
 
   BYTE eventButton;
   switch (aButton) {
     case WidgetMouseEvent::eLeftButton:
       eventButton = VK_LBUTTON;
@@ -4361,25 +4372,25 @@ nsWindow::DispatchMouseEvent(EventMessag
 
       if (rect.Contains(event.mRefPoint)) {
         if (sCurrentWindow == nullptr || sCurrentWindow != this) {
           if ((nullptr != sCurrentWindow) && (!sCurrentWindow->mInDtor)) {
             LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
             sCurrentWindow->DispatchMouseEvent(eMouseExitFromWidget,
                                                wParam, pos, false, 
                                                WidgetMouseEvent::eLeftButton,
-                                               aInputSource, aPointerId);
+                                               aInputSource, aPointerInfo);
           }
           sCurrentWindow = this;
           if (!mInDtor) {
             LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
             sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget,
                                                wParam, pos, false,
                                                WidgetMouseEvent::eLeftButton,
-                                               aInputSource, aPointerId);
+                                               aInputSource, aPointerInfo);
           }
         }
       }
     } else if (aEventMessage == eMouseExitFromWidget) {
       sMouseExitwParam = wParam;
       sMouseExitlParamScreen = lParamToScreen(lParam);
       if (sCurrentWindow == this) {
         sCurrentWindow = nullptr;
@@ -5280,17 +5291,17 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       mp.y      = GET_Y_LPARAM(lParamScreen);
       bool userMovedMouse = false;
       if ((sLastMouseMovePoint.x != mp.x) || (sLastMouseMovePoint.y != mp.y)) {
         userMovedMouse = true;
       }
 
       result = DispatchMouseEvent(eMouseMove, wParam, lParam,
                                   false, WidgetMouseEvent::eLeftButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       if (userMovedMouse) {
         DispatchPendingEvents();
       }
     }
     break;
 
     case WM_NCMOUSEMOVE:
       // If we receive a mouse move event on non-client chrome, make sure and
@@ -5298,26 +5309,26 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       if (mMousePresent && !sIsInMouseCapture)
         SendMessage(mWnd, WM_MOUSELEAVE, 0, 0);
     break;
 
     case WM_LBUTTONDOWN:
     {
       result = DispatchMouseEvent(eMouseDown, wParam, lParam,
                                   false, WidgetMouseEvent::eLeftButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
     }
     break;
 
     case WM_LBUTTONUP:
     {
       result = DispatchMouseEvent(eMouseUp, wParam, lParam,
                                   false, WidgetMouseEvent::eLeftButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
     }
     break;
 
     case WM_MOUSELEAVE:
     {
       if (!mMousePresent)
         break;
@@ -5328,28 +5339,30 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       WPARAM mouseState = (GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0)
         | (GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0)
         | (GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0);
       // Synthesize an event position because we don't get one from
       // WM_MOUSELEAVE.
       LPARAM pos = lParamToClient(::GetMessagePos());
       DispatchMouseEvent(eMouseExitFromWidget, mouseState, pos, false,
                          WidgetMouseEvent::eLeftButton,
-                         MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                         MOUSE_INPUT_SOURCE());
     }
     break;
 
     case MOZ_WM_PEN_LEAVES_HOVER_OF_DIGITIZER:
     {
       LPARAM pos = lParamToClient(::GetMessagePos());
       uint16_t pointerId = InkCollector::sInkCollector->GetPointerId();
       if (pointerId != 0) {
+        WinPointerInfo pointerInfo;
+        pointerInfo.pointerId = pointerId;
         DispatchMouseEvent(eMouseExitFromWidget, wParam, pos, false,
                            WidgetMouseEvent::eLeftButton,
-                           nsIDOMMouseEvent::MOZ_SOURCE_PEN, pointerId);
+                           nsIDOMMouseEvent::MOZ_SOURCE_PEN, &pointerInfo);
         InkCollector::sInkCollector->ClearTarget();
         InkCollector::sInkCollector->ClearPointerId();
       }
     }
     break;
 
     case WM_CONTEXTMENU:
     {
@@ -5375,127 +5388,135 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       {
         pos = lParamToClient(lParam);
       }
 
       result = DispatchMouseEvent(eContextMenu, wParam, pos, contextMenukey,
                                   contextMenukey ?
                                     WidgetMouseEvent::eLeftButton :
                                     WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       if (lParam != -1 && !result && mCustomNonClient &&
           mDraggableRegion.Contains(GET_X_LPARAM(pos), GET_Y_LPARAM(pos))) {
         // Blank area hit, throw up the system menu.
         DisplaySystemMenu(mWnd, mSizeMode, mIsRTL, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
         result = true;
       }
     }
     break;
 
+    case WM_POINTERLEAVE:
+    case WM_POINTERDOWN:
+    case WM_POINTERUP:
+    case WM_POINTERUPDATE:
+      result = OnPointerEvents(msg, wParam, lParam);
+      DispatchPendingEvents();
+      break;
+
     case WM_LBUTTONDBLCLK:
       result = DispatchMouseEvent(eMouseDoubleClick, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eLeftButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_MBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_MBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_MBUTTONDBLCLK:
       result = DispatchMouseEvent(eMouseDoubleClick, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCMBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, 0,
                                   lParamToClient(lParam), false,
                                   WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCMBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, 0,
                                   lParamToClient(lParam), false,
                                   WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCMBUTTONDBLCLK:
       result = DispatchMouseEvent(eMouseDoubleClick, 0,
                                   lParamToClient(lParam), false,
                                   WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_RBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_RBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_RBUTTONDBLCLK:
       result = DispatchMouseEvent(eMouseDoubleClick, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCRBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, 0,
                                   lParamToClient(lParam), false,
                                   WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCRBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, 0,
                                   lParamToClient(lParam), false,
                                   WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCRBUTTONDBLCLK:
       result = DispatchMouseEvent(eMouseDoubleClick, 0,
                                   lParamToClient(lParam), false,
                                   WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                                  MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     // Windows doesn't provide to customize the behavior of 4th nor 5th button
     // of mouse.  If 5-button mouse works with standard mouse deriver of
     // Windows, users cannot disable 4th button (browser back) nor 5th button
     // (browser forward).  We should allow to do it with our prefs since we can
     // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP
@@ -5566,21 +5587,21 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       }
 
       break;
     }
 
     case WM_NCLBUTTONDBLCLK:
       DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
                          false, WidgetMouseEvent::eLeftButton,
-                         MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                         MOUSE_INPUT_SOURCE());
       result = 
         DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam),
                            false, WidgetMouseEvent::eLeftButton,
-                           MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
+                           MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_APPCOMMAND:
     {
       MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
       result = HandleAppCommandMsg(nativeMsg, aRetValue);
       break;
@@ -8018,16 +8039,117 @@ nsWindow::OnWindowedPluginKeyEvent(const
     default:
       // We shouldn't consume WM_*CHAR messages here even if the preceding
       // keydown or keyup event on the plugin is consumed.  It should be
       // managed in each plugin window rather than top level window.
       return NS_OK;
   }
 }
 
+bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam)
+{
+  if (!mPointerEvents.ShouldFireCompatibilityMouseEventsForPen(aWParam)) {
+    // We only handle WM_POINTER* when the input source is pen. This is because
+    // we need some information (e.g. tiltX, tiltY) which can't be retrieved by
+    // WM_*BUTTONDOWN. So we fire Gecko WidgetMouseEvent when handling
+    // WM_POINTER* and consume WM_POINTER* to stop Windows fire WM_*BUTTONDOWN.
+    return false;
+  }
+
+  // When dispatching mouse events with pen, there may be some
+  // WM_POINTERUPDATE messages between WM_POINTERDOWN and WM_POINTERUP with
+  // small movements. Those events will reset sLastMousePoint and reset
+  // sLastClickCount. To prevent that, we keep the last pen down position
+  // and compare it with the subsequent WM_POINTERUPDATE. If the movement is
+  // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing
+  // eMouseMove for WM_POINTERUPDATE.
+  static POINT sLastPointerDownPoint = {0};
+
+  // We don't support chorded buttons for pen. Keep the button at
+  // WM_POINTERDOWN.
+  static WidgetMouseEvent::buttonType sLastPenDownButton =
+    WidgetMouseEvent::eLeftButton;
+  static bool sPointerDown = false;
+
+  EventMessage message;
+  WidgetMouseEvent::buttonType button = WidgetMouseEvent::eLeftButton;
+  switch (msg) {
+  case WM_POINTERDOWN:
+    {
+      LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
+                                      GET_Y_LPARAM(aLParam));
+      sLastPointerDownPoint.x = eventPoint.x;
+      sLastPointerDownPoint.y = eventPoint.y;
+      message = eMouseDown;
+      button = IS_POINTER_SECONDBUTTON_WPARAM(aWParam) ?
+                 WidgetMouseEvent::eRightButton : WidgetMouseEvent::eLeftButton;
+      sLastPenDownButton = button;
+      sPointerDown = true;
+    }
+    break;
+  case WM_POINTERUP:
+    message = eMouseUp;
+    MOZ_ASSERT(sPointerDown, "receive WM_POINTERUP w/o WM_POINTERDOWN");
+    button = sPointerDown ? sLastPenDownButton : WidgetMouseEvent::eLeftButton;
+    sPointerDown = false;
+    break;
+  case WM_POINTERUPDATE:
+    message = eMouseMove;
+    if (sPointerDown) {
+      LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
+                                      GET_Y_LPARAM(aLParam));
+      int32_t movementX = sLastPointerDownPoint.x > eventPoint.x ?
+                            sLastPointerDownPoint.x - eventPoint.x :
+                            eventPoint.x - sLastPointerDownPoint.x;
+      int32_t movementY = sLastPointerDownPoint.y > eventPoint.y ?
+                            sLastPointerDownPoint.y - eventPoint.y :
+                            eventPoint.y - sLastPointerDownPoint.y;
+      bool insideMovementThreshold =
+        movementX < (int32_t)::GetSystemMetrics(SM_CXDRAG) &&
+        movementY < (int32_t)::GetSystemMetrics(SM_CYDRAG);
+
+      if (insideMovementThreshold) {
+        // Suppress firing eMouseMove for WM_POINTERUPDATE if the movement
+        // from last WM_POINTERDOWN is smaller than SM_CXDRAG / SM_CYDRAG
+        return false;
+      }
+      button = sLastPenDownButton;
+    }
+    break;
+  case WM_POINTERLEAVE:
+    message = eMouseExitFromWidget;
+    break;
+  default:
+    return false;
+  }
+  uint32_t pointerId = mPointerEvents.GetPointerId(aWParam);
+  POINTER_PEN_INFO penInfo;
+  mPointerEvents.GetPointerPenInfo(pointerId, &penInfo);
+
+  // Windows defines the pen pressure is normalized to a range between 0 and
+  // 1024. Convert it to float.
+  float pressure = penInfo.pressure ? (float)penInfo.pressure / 1024 : 0;
+  int16_t buttons =
+    sPointerDown ? button == WidgetMouseEvent::eLeftButton ?
+                     WidgetMouseEvent::eLeftButtonFlag :
+                     WidgetMouseEvent::eRightButtonFlag :
+                   WidgetMouseEvent::eNoButtonFlag;
+  WinPointerInfo pointerInfo(pointerId, penInfo.tiltX, penInfo.tiltY, pressure,
+                             buttons);
+
+  // The aLParam of WM_POINTER* is the screen location. Convert it to client
+  // location
+  LPARAM newLParam = lParamToClient(aLParam);
+  DispatchMouseEvent(message, aWParam, newLParam, false, button,
+                     nsIDOMMouseEvent::MOZ_SOURCE_PEN, &pointerInfo);
+  // Consume WM_POINTER* to stop Windows fires WM_*BUTTONDOWN / WM_*BUTTONUP
+  // WM_MOUSEMOVE.
+  return true;
+}
+
 /**************************************************************
  **************************************************************
  **
  ** BLOCK: ChildWindow impl.
  **
  ** Child window overrides.
  **
  **************************************************************
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -26,16 +26,17 @@
 #include "nsRegion.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TimeStamp.h"
 #include "nsMargin.h"
 #include "nsRegionFwd.h"
 
 #include "nsWinGesture.h"
+#include "WinPointerEvents.h"
 #include "WinUtils.h"
 #include "WindowHook.h"
 #include "TaskbarWindowPreview.h"
 
 #ifdef ACCESSIBILITY
 #include "oleacc.h"
 #include "mozilla/a11y/Accessible.h"
 #endif
@@ -224,17 +225,17 @@ public:
                             mozilla::EventMessage aEventMessage,
                             WPARAM wParam,
                             LPARAM lParam,
                             bool aIsContextMenuKey = false,
                             int16_t aButton =
                               mozilla::WidgetMouseEvent::eLeftButton,
                             uint16_t aInputSource =
                               nsIDOMMouseEvent::MOZ_SOURCE_MOUSE,
-                            uint16_t aPointerId = 0);
+                            WinPointerInfo* aPointerInfo = nullptr);
   virtual bool            DispatchWindowEvent(mozilla::WidgetGUIEvent* aEvent,
                                               nsEventStatus& aStatus);
   void                    DispatchPendingEvents();
   bool                    DispatchPluginEvent(UINT aMessage,
                                               WPARAM aWParam,
                                               LPARAM aLParam,
                                               bool aDispatchPendingEvents);
 
@@ -423,16 +424,18 @@ protected:
   bool                    OnTouch(WPARAM wParam, LPARAM lParam);
   bool                    OnHotKey(WPARAM wParam, LPARAM lParam);
   bool                    OnPaint(HDC aDC, uint32_t aNestingLevel);
   void                    OnWindowPosChanged(WINDOWPOS* wp);
   void                    OnWindowPosChanging(LPWINDOWPOS& info);
   void                    OnSysColorChanged();
   void                    OnDPIChanged(int32_t x, int32_t y,
                                        int32_t width, int32_t height);
+  bool                    OnPointerEvents(UINT msg, WPARAM wParam,
+                                          LPARAM lParam);
 
   /**
    * Function that registers when the user has been active (used for detecting
    * when the user is idle).
    */
   void                    UserActivity();
 
   int32_t                 GetHeight(int32_t aProposedHeight);
@@ -649,16 +652,19 @@ protected:
   double mSizeConstraintsScale; // scale in effect when setting constraints
 
   // Used to remember the wParam (i.e. currently pressed modifier keys)
   // and lParam (i.e. last mouse position) in screen coordinates from
   // the previous mouse-exit.  Static since it is not
   // associated with a particular widget (since we exited the widget).
   static WPARAM sMouseExitwParam;
   static LPARAM sMouseExitlParamScreen;
+
+  // Pointer events processing and management
+  WinPointerEvents mPointerEvents;
 };
 
 /**
  * A child window is a window with different style.
  */
 class ChildWindow : public nsWindow {
 
 public: