Bug 550528 - Correct click grouping on Windows (like for quadruple-clicks) draft
authorh-h <henrik.hank@aol.de>
Wed, 04 Jul 2018 18:24:09 +0200
changeset 814132 a40f8505d31dd1381b8b82b36c1b0a9194e587a4
parent 814073 a9dc5dc8e2b8513686ad1b1f28c9e4da6de62226
push id115119
push userbmo:henrik.hank@aol.de
push dateWed, 04 Jul 2018 16:32:23 +0000
bugs550528
milestone63.0a1
Bug 550528 - Correct click grouping on Windows (like for quadruple-clicks) Grouping of clicks, based on temporal and spatial boundaries, means the recognition of double-, triple-, quadruple-, quintuple-clicks etc. Use the "testcase" attachment of the bug which is an HTML file with JavaScript. On Mac, Firefox (allegedly) already allows infinite grouping of clicks. Also, Opera and Chrome allow for infinite grouping of clicks, at least on Windows. Until now, on Windows, only double- and triple-clicks are recognized, which affects selection behavior and renders the config option `about:config?filter=browser.triple_click_selects_paragraph` useless. When `false`, quadruple-clicks should select the paragraph. (Use, e.g., Wikipedia articles for testing.) Furthermore, it prevents us from defining behavior for quintuple-, sextuple-clicks etc. Not only does `nsWindow.cpp` restrict the maximum amount of grouped clicks, but it also contains quite a number of out-of-date and wrong comments in the respective section. Hence, I also restructed the method that does the click grouping for more clarity. I created sections because it is a very long method. (Changing the order of code blocks was necessary anyway, because `TouchEventShouldStartDrag()` expects `eMouseDoubleClick`, if applicable, which, with this patch, Windows does not send anymore, but we have our own recognition that changes `eMouseDown` to `eMouseDoubleClick`.) At first, please take a general look at the changes and answer my questions in the `FIXME` texts. Then, I think, it would be best that I rebase the changes by means of a temporary Git repository to create a series of commits that you can review easier to verify that I didn't make mistakes in this important piece of code. MozReview-Commit-ID: 8wtxtHb8ZwE
widget/MouseEvents.h
widget/windows/nsWindow.cpp
--- a/widget/MouseEvents.h
+++ b/widget/MouseEvents.h
@@ -164,17 +164,17 @@ public:
   // This is set at any mouse event, don't be confused with |button|.
   int16_t buttons;
 
   // Finger or touch pressure of event. It ranges between 0.0 and 1.0.
   float pressure;
   // Touch near a cluster of links (true)
   bool hitCluster;
 
-  // Possible values a in MouseEvent
+  // Possible values in MouseEventBinding.h.
   uint16_t inputSource;
 
   // ID of the canvas HitRegion
   nsString region;
 
   bool IsLeftButtonPressed() const { return !!(buttons & eLeftButtonFlag); }
   bool IsRightButtonPressed() const { return !!(buttons & eRightButtonFlag); }
   bool IsMiddleButtonPressed() const { return !!(buttons & eMiddleButtonFlag); }
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -256,18 +256,18 @@ TriStateBool    nsWindow::sCanQuit      
 HHOOK           nsWindow::sMsgFilterHook          = nullptr;
 HHOOK           nsWindow::sCallProcHook           = nullptr;
 HHOOK           nsWindow::sCallMouseHook          = nullptr;
 bool            nsWindow::sProcessHook            = false;
 UINT            nsWindow::sRollupMsgId            = 0;
 HWND            nsWindow::sRollupMsgWnd           = nullptr;
 UINT            nsWindow::sHookTimerId            = 0;
 
-// Mouse Clicks - static variable definitions for figuring
-// out 1 - 3 Clicks.
+// Mouse clicks - static variable definitions for figuring out single-, double-,
+// triple- and higher-order clicks.
 POINT           nsWindow::sLastMousePoint         = {0};
 POINT           nsWindow::sLastMouseMovePoint     = {0};
 LONG            nsWindow::sLastMouseDownTime      = 0L;
 LONG            nsWindow::sLastClickCount         = 0L;
 BYTE            nsWindow::sLastMouseButton        = 0;
 
 bool            nsWindow::sHaveInitializedPrefs   = false;
 
@@ -1016,31 +1016,33 @@ nsWindow::RegisterWindowClass(const wcha
                               UINT aExtraStyle, LPWSTR aIconID) const
 {
   WNDCLASSW wc;
   if (::GetClassInfoW(nsToolkit::mDllInstance, aClassName, &wc)) {
     // already registered
     return aClassName;
   }
 
-  wc.style         = CS_DBLCLKS | aExtraStyle;
+  // Note: The `CS_DBLCLKS` style may not be used because we implement our own
+  // double-, triple- and higher-order click recognition.
+  wc.style         = aExtraStyle;
   wc.lpfnWndProc   = WinUtils::NonClientDpiScalingDefWindowProcW;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = nsToolkit::mDllInstance;
   wc.hIcon         = aIconID ? ::LoadIconW(::GetModuleHandleW(nullptr), aIconID) : nullptr;
   wc.hCursor       = nullptr;
   wc.hbrBackground = mBrush;
   wc.lpszMenuName  = nullptr;
   wc.lpszClassName = aClassName;
 
   if (!::RegisterClassW(&wc)) {
     // For older versions of Win32 (i.e., not XP), the registration may
     // fail with aExtraStyle, so we have to re-register without it.
-    wc.style = CS_DBLCLKS;
+    wc.style = 0;
     ::RegisterClassW(&wc);
   }
   return aClassName;
 }
 
 static LPWSTR const gStockApplicationIcon = MAKEINTRESOURCEW(32512);
 
 // Return the proper window class for everything except popups.
@@ -2752,17 +2754,17 @@ nsWindow::InvalidateNonClientRegion()
   // +-+-----------------------+-+
   // | | app non-client chrome | |
   // | +-----------------------+ |
   // | |   app client chrome   | | }
   // | +-----------------------+ | }
   // | |      app content      | | } area we don't want to invalidate
   // | +-----------------------+ | }
   // | |   app client chrome   | | }
-  // | +-----------------------+ | 
+  // | +-----------------------+ |
   // +---------------------------+ <
   //  ^                         ^    windows non-client chrome
   // client area = app *
   RECT rect;
   GetWindowRect(mWnd, &rect);
   MapWindowPoints(nullptr, mWnd, (LPPOINT)&rect, 2);
   HRGN winRgn = CreateRectRgnIndirect(&rect);
 
@@ -4352,51 +4354,202 @@ bool nsWindow::TouchEventShouldStartDrag
         node = node->GetParent();
       }
     }
   }
 
   return false;
 }
 
-// Deal with all sort of mouse event
+// Deal with all sorts of mouse events.
 bool
 nsWindow::DispatchMouseEvent(EventMessage aEventMessage, WPARAM wParam,
                              LPARAM lParam, bool aIsContextMenuKey,
                              int16_t aButton, uint16_t aInputSource,
                              WinPointerInfo* aPointerInfo)
 {
-  enum
-  {
-    eUnset,
-    ePrecise,
-    eTouch
-  };
+  /* * * PREPARATION * * */
+
+  enum { eUnset, ePrecise, eTouch };
   static int sTouchInputActiveState = eUnset;
+
   bool result = false;
 
   UserActivity();
 
   if (!mWidgetListener) {
     return result;
   }
 
+  /* * * GATHER EVENT INFO * * */
+
   LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
   LayoutDeviceIntPoint mpScreen = eventPoint + WidgetToScreenOffset();
 
   // Suppress mouse moves caused by widget creation. Make sure to do this early
   // so that we update sLastMouseMovePoint even for touch-induced mousemove events.
   if (aEventMessage == eMouseMove) {
     if ((sLastMouseMovePoint.x == mpScreen.x) && (sLastMouseMovePoint.y == mpScreen.y)) {
       return result;
     }
     sLastMouseMovePoint.x = mpScreen.x;
     sLastMouseMovePoint.y = mpScreen.y;
   }
 
+  WidgetMouseEvent event(true, aEventMessage, this, WidgetMouseEvent::eReal,
+                         aIsContextMenuKey ? WidgetMouseEvent::eContextMenuKey :
+                                             WidgetMouseEvent::eNormal);
+  if (aEventMessage == eContextMenu && aIsContextMenuKey) {
+    LayoutDeviceIntPoint zero(0, 0);
+    InitEvent(event, &zero);
+  } else {
+    InitEvent(event, &eventPoint);
+  }
+
+  if (aEventMessage == eMouseExitFromWidget) {
+    event.mExitFrom =
+      IsTopLevelMouseExit(mWnd) ? WidgetMouseEvent::eTopLevel :
+                                  WidgetMouseEvent::eChild;
+  }
+
+  ModifierKeyState modifierKeyState;
+  modifierKeyState.InitInputEvent(event);
+
+  // eContextMenu with Shift state is special.  It won't fire "contextmenu"
+  // event in the web content for blocking web content to prevent its default.
+  // However, Shift+F10 is a standard shortcut key on Windows.  Therefore,
+  // this should not block web page to prevent its default.  I.e., it should
+  // behave same as ContextMenu key without Shift key.
+  // XXX Should we allow to block web page to prevent its default with
+  //     Ctrl+Shift+F10 or Alt+Shift+F10 instead?
+  if (aEventMessage == eContextMenu && aIsContextMenuKey && event.IsShift() &&
+      NativeKey::LastKeyOrCharMSG().message == WM_SYSKEYDOWN &&
+      NativeKey::LastKeyOrCharMSG().wParam == VK_F10) {
+    event.mModifiers &= ~MODIFIER_SHIFT;
+  }
+
+  event.button = aButton;
+
+  BYTE eventButton;
+  switch (aButton) {
+    case WidgetMouseEvent::eLeftButton:
+      eventButton = VK_LBUTTON;
+      break;
+
+    case WidgetMouseEvent::eMiddleButton:
+      eventButton = VK_MBUTTON;
+      break;
+
+    case WidgetMouseEvent::eRightButton:
+      eventButton = VK_RBUTTON;
+      break;
+
+    default:
+      eventButton = 0;
+      break;
+  }
+
+  event.inputSource = aInputSource;
+
+  /* * * RECOGNIZE DOUBLE-, TRIPLE- OR HIGHER-ORDER CLICK * * */
+
+  // FIXME @ h-h & reviewers:
+  //       `GetSystemMetrics()` is not DPI-aware. Windows 10 introduces
+  //       `GetSystemMetricsForDpi()`. Just scaling the return value of the
+  //       former function seems to be the wrong way because the following
+  //       invocations all return 4*4 px at the moment for the author of this
+  //       todo on a single-monitor system with 200 % DPI scaling; the manifest
+  //       of the test app contained `<dpiAware>true/PM</dpiAware>`:
+  //           GetSystemMetrics      (SM_CXDOUBLECLK),      GetSystemMetrics      (SM_CYDOUBLECLK)
+  //           GetSystemMetricsForDpi(SM_CXDOUBLECLK, 96),  GetSystemMetricsForDpi(SM_CYDOUBLECLK, 96)
+  //           GetSystemMetricsForDpi(SM_CXDOUBLECLK, 192), GetSystemMetricsForDpi(SM_CYDOUBLECLK, 192)
+  //       -> How do we support `GetSystemMetricsForDpi()` without compiler
+  //          errors? How about using `GetProcAddress()` from `kernel32.dll`?
+  //          Is there a predetermined way?
+  //          For exemplary code, see:
+  //          - https://hg.mozilla.org/mozilla-central/file/tip/xpcom/base/nsSystemInfo.cpp#l420
+  //          - https://hg.mozilla.org/mozilla-central/file/tip/widget/windows/WinUtils.cpp#l434
+
+  // Besides a timeframe, there are also spatial boundaries in which two clicks
+  // must happen to be associated with each other (for 2 pairs to form a triple-
+  // click, e.g.). Over the years, Windows' system info function usually
+  // returned a rectangle of 4 * 4 px (without DPI scaling). According to tests,
+  // these boundaries can be visualized this way:
+  //     *  *  *  *  *  *  *  ^
+  //     *  *  *  *  *  *  *  | 4
+  //     *  *  *  *  *  *  *  | px
+  //     *  *  *  +  *  *  *  v ^      + == coordinates of first click
+  //     *  *  *  *  *  *  *    | 4    * == valid coordinates for second click
+  //     *  *  *  *  *  *  *    | px
+  //     *  *  *  *  *  *  *    v
+  //     <-------->  4 px
+  //        4 px  <-------->
+  short horzMovementThreshold, vertMovementThreshold;
+  //if (IsWin10AnniversaryUpdateOrLater()) {
+  //  int32_t dpi = NSToIntRound(GetDPI());
+  //  horzMovementThreshold = (short)::GetSystemMetricsForDpi(SM_CXDOUBLECLK, dpi);
+  //  vertMovementThreshold = (short)::GetSystemMetricsForDpi(SM_CYDOUBLECLK, dpi);
+  //} else {
+    horzMovementThreshold = (short)::GetSystemMetrics(SM_CXDOUBLECLK);
+    vertMovementThreshold = (short)::GetSystemMetrics(SM_CYDOUBLECLK);
+  //}
+  bool insideMovementThreshold =
+    DeprecatedAbs(sLastMousePoint.x - eventPoint.x) < horzMovementThreshold &&
+    DeprecatedAbs(sLastMousePoint.y - eventPoint.y) < vertMovementThreshold;
+
+  // We're going to time click pairs from mouse-down to mouse-down.
+  LONG curMsgTime = ::GetMessageTime();
+
+  switch (aEventMessage) {
+    case eMouseUp:
+      // On next mouse-down, relate to this event data.
+      sLastMousePoint.x = eventPoint.x;
+      sLastMousePoint.y = eventPoint.y;
+      sLastMouseButton = eventButton;
+      break;
+
+    case eMouseDown:
+      // Associate click with previously grouped ones? (Forming double-, triple-
+      // or higher-order click depending on temporal and spatial boundaries.)
+      if (curMsgTime - sLastMouseDownTime < (LONG)::GetDoubleClickTime() &&
+          insideMovementThreshold &&
+          eventButton == sLastMouseButton) {
+        sLastClickCount++;
+        if (sLastClickCount == 2) {
+          aEventMessage = eMouseDoubleClick;
+          // Note: `event.mMessage` must remain `eMouseDown`!
+        }
+      } else {
+        // Reset - boundaries were not met.
+        sLastClickCount = 1;
+      }
+
+      // Save last click timestamp only on mouse-down.
+      sLastMouseDownTime = curMsgTime;
+      break;
+
+    case eMouseMove:
+      if (!insideMovementThreshold) {
+        // Reset - mouse has left spatial boundaries.
+        sLastClickCount = 0;
+      }
+      break;
+
+    default:
+      break;
+  }
+  event.mClickCount = sLastClickCount;
+
+#ifdef NS_DEBUG_XX
+  MOZ_LOG(gWindowsLog, LogLevel::Info,
+         ("Msg Time: %d Click Count: %d\n", curMsgTime, event.mClickCount));
+#endif
+
+  /* * * ADDRESS TOUCH AND PEN SUPPORT * * */
+
   if (WinUtils::GetIsMouseFromTouch(aEventMessage)) {
     if (aEventMessage == eMouseDown) {
       Telemetry::Accumulate(Telemetry::FX_TOUCH_USED, 1);
     }
 
     // Fire an observer when the user initially touches a touch screen. Front end
     // uses this to modify UX.
     if (sTouchInputActiveState != eTouch) {
@@ -4410,17 +4563,17 @@ nsWindow::DispatchMouseEvent(EventMessag
       // If mTouchWindow is true, then we must have APZ enabled and be
       // feeding it raw touch events. In that case we only want to
       // send touch-generated mouse events to content if they should
       // start a touch-based drag-and-drop gesture, such as on
       // double-tapping or when tapping elements marked with the
       // touchdownstartsdrag attribute in chrome UI.
       MOZ_ASSERT(mAPZC);
       if (TouchEventShouldStartDrag(aEventMessage, eventPoint)) {
-        aEventMessage = eMouseTouchDrag;
+        event.mMessage = aEventMessage = eMouseTouchDrag;
       } else {
         return result;
       }
     }
   } else {
     // Fire an observer when the user initially uses a mouse or pen.
     if (sTouchInputActiveState != ePrecise) {
       sTouchInputActiveState = ePrecise;
@@ -4430,258 +4583,192 @@ nsWindow::DispatchMouseEvent(EventMessag
     }
   }
 
   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 (MouseEvent_Binding::MOZ_SOURCE_PEN == aInputSource
+  if (aInputSource == MouseEvent_Binding::MOZ_SOURCE_PEN &&
       // 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) {
+      mWindowType == nsWindowType::eWindowType_toplevel &&
+      // Currently this scheme is used only when pointer events are enabled.
+      gfxPrefs::PointerEventsEnabled() && InkCollector::sInkCollector) {
     InkCollector::sInkCollector->SetTarget(mWnd);
     InkCollector::sInkCollector->SetPointerId(pointerId);
   }
 
+  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;
+  }
+
+  /* * * GATHER EVENT DATA FOR PLUGINS * * */
+
+  NPEvent pluginEvent;
+
+  switch (aEventMessage) {
+    case eMouseDown:
+      switch (aButton) {
+        case WidgetMouseEvent::eLeftButton:
+          pluginEvent.event = WM_LBUTTONDOWN;
+          break;
+
+        case WidgetMouseEvent::eMiddleButton:
+          pluginEvent.event = WM_MBUTTONDOWN;
+          break;
+
+        case WidgetMouseEvent::eRightButton:
+          pluginEvent.event = WM_RBUTTONDOWN;
+          break;
+
+        default:
+          break;
+      }
+      break;
+
+    case eMouseUp:
+      switch (aButton) {
+        case WidgetMouseEvent::eLeftButton:
+          pluginEvent.event = WM_LBUTTONUP;
+          break;
+
+        case WidgetMouseEvent::eMiddleButton:
+          pluginEvent.event = WM_MBUTTONUP;
+          break;
+
+        case WidgetMouseEvent::eRightButton:
+          pluginEvent.event = WM_RBUTTONUP;
+          break;
+
+        default:
+          break;
+      }
+      break;
+
+    case eMouseDoubleClick:
+      switch (aButton) {
+        case WidgetMouseEvent::eLeftButton:
+          pluginEvent.event = WM_LBUTTONDBLCLK;
+          break;
+
+        case WidgetMouseEvent::eMiddleButton:
+          pluginEvent.event = WM_MBUTTONDBLCLK;
+          break;
+
+        case WidgetMouseEvent::eRightButton:
+          pluginEvent.event = WM_RBUTTONDBLCLK;
+          break;
+
+        default:
+          break;
+      }
+      break;
+
+    case eMouseMove:
+      pluginEvent.event = WM_MOUSEMOVE;
+      break;
+
+    case eMouseExitFromWidget:
+      pluginEvent.event = WM_MOUSELEAVE;
+      break;
+
+    default:
+      pluginEvent.event = WM_NULL;
+      break;
+  }
+
+  // Plugins need raw OS-event flags!
+  pluginEvent.wParam = wParam;
+  pluginEvent.lParam = lParam;
+
+  event.mPluginEvent.Copy(pluginEvent);
+
+  /* * * MOUSE CAPTURE * * */
+
   switch (aEventMessage) {
     case eMouseDown:
       CaptureMouse(true);
       break;
 
     // eMouseMove and eMouseExitFromWidget are here because we need to make
     // sure capture flag isn't left on after a drag where we wouldn't see a
     // button up message (see bug 324131).
     case eMouseUp:
     case eMouseMove:
     case eMouseExitFromWidget:
-      if (!(wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) && sIsInMouseCapture)
+      if (sIsInMouseCapture &&
+          // No button pressed.
+          !(wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) {
         CaptureMouse(false);
+      }
       break;
 
     default:
       break;
-
-  } // switch
-
-  WidgetMouseEvent event(true, aEventMessage, this, WidgetMouseEvent::eReal,
-                         aIsContextMenuKey ? WidgetMouseEvent::eContextMenuKey :
-                                             WidgetMouseEvent::eNormal);
-  if (aEventMessage == eContextMenu && aIsContextMenuKey) {
-    LayoutDeviceIntPoint zero(0, 0);
-    InitEvent(event, &zero);
-  } else {
-    InitEvent(event, &eventPoint);
-  }
-
-  ModifierKeyState modifierKeyState;
-  modifierKeyState.InitInputEvent(event);
-
-  // eContextMenu with Shift state is special.  It won't fire "contextmenu"
-  // event in the web content for blocking web content to prevent its default.
-  // However, Shift+F10 is a standard shortcut key on Windows.  Therefore,
-  // this should not block web page to prevent its default.  I.e., it should
-  // behave same as ContextMenu key without Shift key.
-  // XXX Should we allow to block web page to prevent its default with
-  //     Ctrl+Shift+F10 or Alt+Shift+F10 instead?
-  if (aEventMessage == eContextMenu && aIsContextMenuKey && event.IsShift() &&
-      NativeKey::LastKeyOrCharMSG().message == WM_SYSKEYDOWN &&
-      NativeKey::LastKeyOrCharMSG().wParam == VK_F10) {
-    event.mModifiers &= ~MODIFIER_SHIFT;
-  }
-
-  event.button    = aButton;
-  event.inputSource = aInputSource;
-  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;
-      break;
-    case WidgetMouseEvent::eMiddleButton:
-      eventButton = VK_MBUTTON;
-      break;
-    case WidgetMouseEvent::eRightButton:
-      eventButton = VK_RBUTTON;
-      break;
-    default:
-      eventButton = 0;
-      break;
-  }
-
-  // Doubleclicks are used to set the click count, then changed to mousedowns
-  // We're going to time double-clicks from mouse *up* to next mouse *down*
-  LONG curMsgTime = ::GetMessageTime();
-
-  switch (aEventMessage) {
-    case eMouseDoubleClick:
-      event.mMessage = eMouseDown;
-      event.button = aButton;
-      sLastClickCount = 2;
-      sLastMouseDownTime = curMsgTime;
-      break;
-    case eMouseUp:
-      // remember when this happened for the next mouse down
-      sLastMousePoint.x = eventPoint.x;
-      sLastMousePoint.y = eventPoint.y;
-      sLastMouseButton = eventButton;
-      break;
-    case eMouseDown:
-      // now look to see if we want to convert this to a double- or triple-click
-      if (((curMsgTime - sLastMouseDownTime) < (LONG)::GetDoubleClickTime()) &&
-          insideMovementThreshold &&
-          eventButton == sLastMouseButton) {
-        sLastClickCount ++;
-      } else {
-        // reset the click count, to count *this* click
-        sLastClickCount = 1;
-      }
-      // Set last Click time on MouseDown only
-      sLastMouseDownTime = curMsgTime;
-      break;
-    case eMouseMove:
-      if (!insideMovementThreshold) {
-        sLastClickCount = 0;
-      }
-      break;
-    case eMouseExitFromWidget:
-      event.mExitFrom =
-        IsTopLevelMouseExit(mWnd) ? WidgetMouseEvent::eTopLevel :
-                                    WidgetMouseEvent::eChild;
-      break;
-    default:
-      break;
-  }
-  event.mClickCount = sLastClickCount;
-
-#ifdef NS_DEBUG_XX
-  MOZ_LOG(gWindowsLog, LogLevel::Info,
-         ("Msg Time: %d Click Count: %d\n", curMsgTime, event.mClickCount));
-#endif
-
-  NPEvent pluginEvent;
-
-  switch (aEventMessage) {
-    case eMouseDown:
-      switch (aButton) {
-        case WidgetMouseEvent::eLeftButton:
-          pluginEvent.event = WM_LBUTTONDOWN;
-          break;
-        case WidgetMouseEvent::eMiddleButton:
-          pluginEvent.event = WM_MBUTTONDOWN;
-          break;
-        case WidgetMouseEvent::eRightButton:
-          pluginEvent.event = WM_RBUTTONDOWN;
-          break;
-        default:
-          break;
-      }
-      break;
-    case eMouseUp:
-      switch (aButton) {
-        case WidgetMouseEvent::eLeftButton:
-          pluginEvent.event = WM_LBUTTONUP;
-          break;
-        case WidgetMouseEvent::eMiddleButton:
-          pluginEvent.event = WM_MBUTTONUP;
-          break;
-        case WidgetMouseEvent::eRightButton:
-          pluginEvent.event = WM_RBUTTONUP;
-          break;
-        default:
-          break;
-      }
-      break;
-    case eMouseDoubleClick:
-      switch (aButton) {
-        case WidgetMouseEvent::eLeftButton:
-          pluginEvent.event = WM_LBUTTONDBLCLK;
-          break;
-        case WidgetMouseEvent::eMiddleButton:
-          pluginEvent.event = WM_MBUTTONDBLCLK;
-          break;
-        case WidgetMouseEvent::eRightButton:
-          pluginEvent.event = WM_RBUTTONDBLCLK;
-          break;
-        default:
-          break;
-      }
-      break;
-    case eMouseMove:
-      pluginEvent.event = WM_MOUSEMOVE;
-      break;
-    case eMouseExitFromWidget:
-      pluginEvent.event = WM_MOUSELEAVE;
-      break;
-    default:
-      pluginEvent.event = WM_NULL;
-      break;
-  }
-
-  pluginEvent.wParam = wParam;     // plugins NEED raw OS event flags!
-  pluginEvent.lParam = lParam;
-
-  event.mPluginEvent.Copy(pluginEvent);
-
-  // call the event callback
+  }
+
+  /* * * INFORM WIDGET * * */
+
+  // Call the event callback.
   if (mWidgetListener) {
-    if (aEventMessage == eMouseMove) {
-      LayoutDeviceIntRect rect = GetBounds();
-      rect.MoveTo(0, 0);
-
-      if (rect.Contains(event.mRefPoint)) {
-        if (sCurrentWindow == nullptr || sCurrentWindow != this) {
+    switch (aEventMessage) {
+      case eMouseMove: {
+        LayoutDeviceIntRect rect = GetBounds();
+        rect.MoveTo(0, 0);
+
+        if (rect.Contains(event.mRefPoint) &&
+            (sCurrentWindow == nullptr || sCurrentWindow != this)) {
           if ((nullptr != sCurrentWindow) && (!sCurrentWindow->mInDtor)) {
             LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
             sCurrentWindow->DispatchMouseEvent(eMouseExitFromWidget,
-                                               wParam, pos, false, 
+                                               wParam, pos, false,
                                                WidgetMouseEvent::eLeftButton,
                                                aInputSource, aPointerInfo);
           }
           sCurrentWindow = this;
           if (!mInDtor) {
             LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
             sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget,
                                                wParam, pos, false,
                                                WidgetMouseEvent::eLeftButton,
                                                aInputSource, aPointerInfo);
           }
         }
+        break;
       }
-    } else if (aEventMessage == eMouseExitFromWidget) {
-      if (sCurrentWindow == this) {
-        sCurrentWindow = nullptr;
-      }
+      case eMouseExitFromWidget:
+        if (sCurrentWindow == this) {
+          sCurrentWindow = nullptr;
+        }
+        break;
+
+      default:
+        break;
     }
 
     result = ConvertStatus(DispatchInputEvent(&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;
 }
 
 void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate)
 {
   if (aIsActivate)
     sJustGotActivate = false;
   sJustGotDeactivate = false;
@@ -5744,72 +5831,48 @@ nsWindow::ProcessMessage(UINT msg, WPARA
     case WM_POINTERUP:
     case WM_POINTERUPDATE:
       result = OnPointerEvents(msg, wParam, lParam);
       if (result) {
         DispatchPendingEvents();
       }
       break;
 
-    case WM_LBUTTONDBLCLK:
-      result = DispatchMouseEvent(eMouseDoubleClick, wParam,
-                                  lParam, false,
-                                  WidgetMouseEvent::eLeftButton,
-                                  MOUSE_INPUT_SOURCE());
-      DispatchPendingEvents();
-      break;
-
     case WM_MBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eMiddleButton,
                                   MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_MBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eMiddleButton,
                                   MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
-    case WM_MBUTTONDBLCLK:
-      result = DispatchMouseEvent(eMouseDoubleClick, wParam,
-                                  lParam, false,
-                                  WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE());
-      DispatchPendingEvents();
-      break;
-
     case WM_NCMBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, 0,
                                   lParamToClient(lParam), false,
                                   WidgetMouseEvent::eMiddleButton,
                                   MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCMBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, 0,
                                   lParamToClient(lParam), false,
                                   WidgetMouseEvent::eMiddleButton,
                                   MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
-    case WM_NCMBUTTONDBLCLK:
-      result = DispatchMouseEvent(eMouseDoubleClick, 0,
-                                  lParamToClient(lParam), false,
-                                  WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE());
-      DispatchPendingEvents();
-      break;
-
     case WM_RBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eRightButton,
                                   MOUSE_INPUT_SOURCE(),
                                   mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
       break;
@@ -5818,48 +5881,32 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       result = DispatchMouseEvent(eMouseUp, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eRightButton,
                                   MOUSE_INPUT_SOURCE(),
                                   mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
       break;
 
-    case WM_RBUTTONDBLCLK:
-      result = DispatchMouseEvent(eMouseDoubleClick, wParam,
-                                  lParam, false,
-                                  WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE());
-      DispatchPendingEvents();
-      break;
-
     case WM_NCRBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, 0,
                                   lParamToClient(lParam), false,
                                   WidgetMouseEvent::eRightButton,
                                   MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCRBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, 0,
                                   lParamToClient(lParam), false,
                                   WidgetMouseEvent::eRightButton,
                                   MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
-    case WM_NCRBUTTONDBLCLK:
-      result = DispatchMouseEvent(eMouseDoubleClick, 0,
-                                  lParamToClient(lParam), false,
-                                  WidgetMouseEvent::eRightButton,
-                                  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
     // messages are not sent to DefWindowProc.
     case WM_XBUTTONDOWN:
     case WM_XBUTTONUP:
@@ -5930,27 +5977,28 @@ nsWindow::ProcessMessage(UINT msg, WPARA
     {
       ScreenHelperWin::RefreshScreens();
       if (mWidgetListener) {
         mWidgetListener->UIResolutionChanged();
       }
       break;
     }
 
-    case WM_NCLBUTTONDBLCLK:
-      DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
-                         false, WidgetMouseEvent::eLeftButton,
-                         MOUSE_INPUT_SOURCE());
-      result = 
-        DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam),
-                           false, WidgetMouseEvent::eLeftButton,
-                           MOUSE_INPUT_SOURCE());
-      DispatchPendingEvents();
-      break;
-
+//FIXME @ h-h & reviewers: Unlike the M and R button equivalents, the `WM_ NC L BUTTON DBLCLK` event has no `WM_ NC L BUTTON DOWN` and `WM_ NC L BUTTON UP` companions in this switch block; and its code looks different to the other deleted code in this switch block. Can it be removed safely?
+//    case WM_NCLBUTTONDBLCLK:
+//      DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
+//                         false, WidgetMouseEvent::eLeftButton,
+//                         MOUSE_INPUT_SOURCE());
+//      result =
+//        DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam),
+//                           false, WidgetMouseEvent::eLeftButton,
+//                           MOUSE_INPUT_SOURCE());
+//      DispatchPendingEvents();
+//      break;
+//
     case WM_APPCOMMAND:
     {
       MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
       result = HandleAppCommandMsg(nativeMsg, aRetValue);
       break;
     }
 
     // The WM_ACTIVATE event is fired when a window is raised or lowered,