@@ -331,10 +331,6 @@ UINT nsWindow::sRollupMsgId = 0;
331
331
HWND nsWindow::sRollupMsgWnd = nullptr ;
332
332
UINT nsWindow::sHookTimerId = 0 ;
333
333
334
- // Used to prevent dispatching mouse events that do not originate from user
335
- // input.
336
- POINT nsWindow::sLastMouseMovePoint = {0 };
337
-
338
334
bool nsWindow::sIsRestoringSession = false ;
339
335
340
336
bool nsWindow::sTouchInjectInitialized = false ;
@@ -443,6 +439,161 @@ static bool gInitializedVirtualDesktopManager = false;
443
439
// coordinate this many milliseconds:
444
440
#define HITTEST_CACHE_LIFETIME_MS 50
445
441
442
+ /* *
443
+ * Used to prevent dispatching eMouseMove events that do not originate from user
444
+ * input.
445
+ */
446
+ class nsWindow ::LastMouseMoveData {
447
+ public:
448
+ /* *
449
+ * Return true if eMouseMove whose point is aPoint, input source is
450
+ * aInputSource and pointerId is aPointerId should not be dispatched to avoid
451
+ * unexpected behavior in content.
452
+ *
453
+ * @param aPoint The ref-point where the new mouse move occurred.
454
+ * @param aInputSource The input source of the mouse move event.
455
+ * @param aPointerId The pointerId of the mouse move event. If there is
456
+ * no specific one because of a mouse input, specify 0
457
+ * which won't be referred anyway.
458
+ * @return true if the event should not cause eMouseMove event in the content.
459
+ */
460
+ template <typename POINTOrLayoutDeviceIntPoint>
461
+ [[nodiscard]] static bool ShouldIgnoreMouseMoveOf (
462
+ const POINTOrLayoutDeviceIntPoint& aPoint, uint16_t aInputSource,
463
+ uint32_t aPointerId) {
464
+ return sInstance .ShouldIgnoreMouseMoveOfImpl (aPoint, aInputSource,
465
+ aPointerId);
466
+ }
467
+
468
+ /* *
469
+ * Forget the last mouse move point caused by both mouse and non-mouse.
470
+ */
471
+ static void Clear () { sInstance .ClearImpl (); }
472
+
473
+ /* *
474
+ * Called when nsWindow will dispatch an eMouseMove event.
475
+ *
476
+ * @param aPoint The ref-point where the native mouse move occurred.
477
+ * @param aInputSource The input source of the mouse move event.
478
+ * @param aPointerId The pointerId of the mouse move event. If there is
479
+ * no specific one because of a mouse input, specify 0
480
+ * which won't be referred anyway.
481
+ */
482
+ static void WillDispatchMouseMoveOf (const LayoutDeviceIntPoint& aPoint,
483
+ uint16_t aInputSource,
484
+ uint32_t aPointerId) {
485
+ sInstance .WillDispatchMouseMoveOfImpl (aPoint, aInputSource, aPointerId);
486
+ }
487
+
488
+ private:
489
+ LastMouseMoveData () = default ;
490
+
491
+ template <typename POINTOrLayoutDeviceIntPoint>
492
+ [[nodiscard]] bool ShouldIgnoreMouseMoveOfImpl (
493
+ const POINTOrLayoutDeviceIntPoint& aPoint, uint16_t aInputSource,
494
+ uint32_t aPointerId) const {
495
+ // Suppress mouse moves caused by widget creation which is fired at same
496
+ // screen position with the last mouse position. And also we should ignore
497
+ // odd mouse move events which are caused by some tablet drivers. They try
498
+ // to restore the mouse position and restore the pen position again
499
+ // continuously. We don't want such events for avoiding the mouse cursor to
500
+ // flicker, avoiding to dispatching synthesized mouse/pointer boundary
501
+ // events and avoiding to switch `:hover` state because they waste a lot of
502
+ // CPU resource. So, let's ignore native mouse move events which occurs at
503
+ // the same position of the last point with the same pointer.
504
+ return PointsEqual (LastPoint (aInputSource, aPointerId), aPoint);
505
+ }
506
+
507
+ void ClearImpl () {
508
+ mLastPointByMouse .reset ();
509
+ mLastPointAndPointerIdByNonMouse .reset ();
510
+ }
511
+
512
+ void WillDispatchMouseMoveOfImpl (const LayoutDeviceIntPoint& aPoint,
513
+ uint16_t aInputSource, uint32_t aPointerId) {
514
+ POINT& lastPoint = [&]() -> POINT& {
515
+ if (IsMouse (aInputSource)) {
516
+ if (mLastPointByMouse .isNothing ()) {
517
+ mLastPointByMouse .emplace ();
518
+ }
519
+ return mLastPointByMouse .ref ();
520
+ }
521
+ if (mLastPointAndPointerIdByNonMouse .isNothing ()) {
522
+ mLastPointAndPointerIdByNonMouse .emplace ();
523
+ }
524
+ mLastPointAndPointerIdByNonMouse ->mPointerId = aPointerId;
525
+ return mLastPointAndPointerIdByNonMouse ->mPoint ;
526
+ }();
527
+ lastPoint.x = aPoint.x .value ;
528
+ lastPoint.y = aPoint.y .value ;
529
+ }
530
+
531
+ /* *
532
+ * Return true if aInputSource is "mouse".
533
+ */
534
+ [[nodiscard]] static bool IsMouse (uint16_t aInputSource) {
535
+ return aInputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE;
536
+ }
537
+
538
+ /* *
539
+ * Return true if aPoint and aOtherPoint are the same point.
540
+ */
541
+ template <typename PointType>
542
+ [[nodiscard]] static bool PointsEqual (const Maybe<POINT>& aPoint,
543
+ const PointType& aOtherPoint);
544
+
545
+ /* *
546
+ * Return the last mouse move event position of aPointerId if aInputSource is
547
+ * not "mouse" or of the mouse if aInputSource is "mouse".
548
+ */
549
+ [[nodiscard]] Maybe<POINT> LastPoint (uint16_t aInputSource,
550
+ uint32_t aPointerId) const {
551
+ return IsMouse (aInputSource)
552
+ ? mLastPointByMouse
553
+ : (IsLastNonMousePointerId (aPointerId)
554
+ ? Some (mLastPointAndPointerIdByNonMouse ->mPoint )
555
+ : Nothing ());
556
+ }
557
+
558
+ /* *
559
+ * Return true if aPointerId is same as the last non-mouse pointerId.
560
+ */
561
+ [[nodiscard]] bool IsLastNonMousePointerId (uint32_t aPointerId) const {
562
+ return mLastPointAndPointerIdByNonMouse &&
563
+ mLastPointAndPointerIdByNonMouse ->mPointerId == aPointerId;
564
+ }
565
+
566
+ // We don't need to take care of pointerId of mouse because it's ignored by
567
+ // PresShell, PointerEventHandler and EventStateManager to handle mouse
568
+ // boundary events and CSS hover state.
569
+ Maybe<POINT> mLastPointByMouse ;
570
+ // We need to manage the last non-mouse pointer location and pointerId as a
571
+ // pair because pointerId is meaningful for the non-mouse input sources.
572
+ struct LastNonMousePointerMoveData {
573
+ POINT mPoint = {0 };
574
+ uint32_t mPointerId = 0 ;
575
+ };
576
+ Maybe<LastNonMousePointerMoveData> mLastPointAndPointerIdByNonMouse ;
577
+
578
+ static LastMouseMoveData sInstance ;
579
+ };
580
+
581
+ template <>
582
+ bool nsWindow::LastMouseMoveData::PointsEqual (const Maybe<POINT>& aPoint,
583
+ const POINT& aOtherPoint) {
584
+ return aPoint.isSome () && aPoint->x == aOtherPoint.x &&
585
+ aPoint->y == aOtherPoint.y ;
586
+ }
587
+
588
+ template <>
589
+ bool nsWindow::LastMouseMoveData::PointsEqual (
590
+ const Maybe<POINT>& aPoint, const LayoutDeviceIntPoint& aOtherPoint) {
591
+ return aPoint.isSome () && aPoint->x == aOtherPoint.x .value &&
592
+ aPoint->y == aOtherPoint.y .value ;
593
+ }
594
+
595
+ nsWindow::LastMouseMoveData nsWindow::LastMouseMoveData::sInstance ;
596
+
446
597
#if defined(ACCESSIBILITY)
447
598
448
599
namespace mozilla {
@@ -4052,15 +4203,16 @@ bool nsWindow::DispatchMouseEvent(EventMessage aEventMessage, WPARAM wParam,
4052
4203
LayoutDeviceIntPoint mpScreen = eventPoint + WidgetToScreenOffset ();
4053
4204
4054
4205
// Suppress mouse moves caused by widget creation. Make sure to do this early
4055
- // so that we update sLastMouseMovePoint even for touch-induced mousemove
4056
- // events.
4206
+ // so that we update sLastMouseMovePointByAnyPointer even for touch-induced
4207
+ // mousemove events.
4057
4208
if (aEventMessage == eMouseMove) {
4058
- if ((sLastMouseMovePoint .x == mpScreen.x .value ) &&
4059
- (sLastMouseMovePoint .y == mpScreen.y .value )) {
4209
+ if (LastMouseMoveData::ShouldIgnoreMouseMoveOf (
4210
+ mpScreen, aInputSource,
4211
+ aPointerInfo ? aPointerInfo->pointerId : 0 )) {
4060
4212
return result;
4061
4213
}
4062
- sLastMouseMovePoint . x = mpScreen. x ;
4063
- sLastMouseMovePoint . y = mpScreen. y ;
4214
+ LastMouseMoveData::WillDispatchMouseMoveOf (
4215
+ mpScreen, aInputSource, aPointerInfo ? aPointerInfo-> pointerId : 0 ) ;
4064
4216
}
4065
4217
4066
4218
if (!bool (aIsNonclient) && WinUtils::GetIsMouseFromTouch (aEventMessage)) {
@@ -5055,16 +5207,14 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam,
5055
5207
POINT mp;
5056
5208
mp.x = GET_X_LPARAM (lParamScreen);
5057
5209
mp.y = GET_Y_LPARAM (lParamScreen);
5058
- bool userMovedMouse = false ;
5059
- if ((sLastMouseMovePoint .x != mp.x ) || (sLastMouseMovePoint .y != mp.y )) {
5060
- userMovedMouse = true ;
5061
- }
5062
-
5063
- if (userMovedMouse) {
5064
- result = DispatchMouseEvent (
5065
- eMouseMove, wParam, lParam, false , MouseButton::ePrimary,
5066
- MOUSE_INPUT_SOURCE (),
5067
- mPointerEvents .GetCachedPointerInfo (msg, wParam));
5210
+ const uint16_t inputSource = MOUSE_INPUT_SOURCE ();
5211
+ WinPointerInfo* const pointerInfo =
5212
+ mPointerEvents .GetCachedPointerInfo (msg, wParam);
5213
+ if (!LastMouseMoveData::ShouldIgnoreMouseMoveOf (
5214
+ mp, inputSource, pointerInfo ? pointerInfo->pointerId : 0 )) {
5215
+ result =
5216
+ DispatchMouseEvent (eMouseMove, wParam, lParam, false ,
5217
+ MouseButton::ePrimary, inputSource, pointerInfo);
5068
5218
DispatchPendingEvents ();
5069
5219
}
5070
5220
} break ;
@@ -5142,11 +5292,11 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam,
5142
5292
mMousePresent = false ;
5143
5293
5144
5294
// Check if the mouse is over the fullscreen transition window, if so
5145
- // clear sLastMouseMovePoint. This way the WM_MOUSEMOVE we get after the
5295
+ // clear LastMouseMoveData. This way the WM_MOUSEMOVE we get after the
5146
5296
// transition window disappears will not be ignored, even if the mouse
5147
5297
// hasn't moved.
5148
5298
if (mTransitionWnd && WindowAtMouse () == mTransitionWnd ) {
5149
- sLastMouseMovePoint = { 0 } ;
5299
+ LastMouseMoveData::Clear () ;
5150
5300
}
5151
5301
5152
5302
// We need to check mouse button states and put them in for
@@ -6167,10 +6317,10 @@ nsresult nsWindow::SynthesizeNativeMouseEvent(
6167
6317
switch (aNativeMessage) {
6168
6318
case NativeMouseMessage::Move:
6169
6319
input.mi .dwFlags = MOUSEEVENTF_MOVE;
6170
- // Reset sLastMouseMovePoint so that even if we're moving the mouse
6171
- // to the position it's already at, we still dispatch a mousemove
6172
- // event, because the callers of this function expect that.
6173
- sLastMouseMovePoint = { 0 } ;
6320
+ // Reset LastMouseMoveData so that even if we're moving the mouse to the
6321
+ // position it's already at, we still dispatch a mousemove event, because
6322
+ // the callers of this function expect that.
6323
+ LastMouseMoveData::Clear () ;
6174
6324
break ;
6175
6325
case NativeMouseMessage::ButtonDown:
6176
6326
case NativeMouseMessage::ButtonUp: {
0 commit comments