Bug 1534389 - Send normal mouse events when cursor is over a draggable region on Windows. r=jmathies
☠☠ backed out by 1c130758d4d6 ☠ ☠
authorMike Conley <mconley@mozilla.com>
Mon, 18 Mar 2019 20:02:27 +0000
changeset 464884 c26e947b78c28aebdfe2e6e96331f8e90de1d9f9
parent 464883 4b4f6ca7d62419c5f45835b0c145dae295c5f936
child 464885 0f2bb770f3acf22333e843dc8e60d761dc3158ed
push id35727
push userdvarga@mozilla.com
push dateTue, 19 Mar 2019 09:48:59 +0000
treeherdermozilla-central@70baa37ae1eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmathies
bugs1534389
milestone68.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 1534389 - Send normal mouse events when cursor is over a draggable region on Windows. r=jmathies Differential Revision: https://phabricator.services.mozilla.com/D23231
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -588,16 +588,17 @@ nsWindow::nsWindow(bool aIsChildWindow)
   mUnicodeWidget = true;
   mDisplayPanFeedback = false;
   mTouchWindow = false;
   mFutureMarginsToUse = false;
   mCustomNonClient = false;
   mHideChrome = false;
   mFullscreenMode = false;
   mMousePresent = false;
+  mMouseInDraggableArea = false;
   mDestroyCalled = false;
   mIsEarlyBlankWindow = false;
   mHasTaskbarIconBeenCreated = false;
   mMouseTransparent = false;
   mPickerDisplayCount = 0;
   mWindowType = eWindowType_child;
   mBorderStyle = eBorderStyle_default;
   mOldSizeMode = nsSizeMode_Normal;
@@ -608,17 +609,17 @@ nsWindow::nsWindow(bool aIsChildWindow)
   mOldExStyle = 0;
   mPainting = 0;
   mLastKeyboardLayout = 0;
   mBlurSuppressLevel = 0;
   mLastPaintEndTime = TimeStamp::Now();
   mCachedHitTestPoint.x = 0;
   mCachedHitTestPoint.y = 0;
   mCachedHitTestTime = TimeStamp::Now();
-  mCachedHitTestResult = 0;
+  mCachedHitTestResult = false;
 #ifdef MOZ_XUL
   mTransparencyMode = eTransparencyOpaque;
   memset(&mGlassMargins, 0, sizeof mGlassMargins);
 #endif
   DWORD background = ::GetSysColor(COLOR_BTNFACE);
   mBrush = ::CreateSolidBrush(NSRGB_2_COLOREF(background));
   mSendingSetText = false;
   mDefaultScale = -1.0;  // not yet set, will be calculated on first use
@@ -5295,16 +5296,18 @@ bool nsWindow::ProcessMessage(UINT msg, 
     case WM_ERASEBKGND:
       if (!AutoErase((HDC)wParam)) {
         *aRetValue = 1;
         result = true;
       }
       break;
 
     case WM_MOUSEMOVE: {
+      mMouseInDraggableArea =
+          WithinDraggableRegion(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
       if (!mMousePresent && !sIsInMouseCapture) {
         // First MOUSEMOVE over the client area. Ask for MOUSELEAVE
         TRACKMOUSEEVENT mTrack;
         mTrack.cbSize = sizeof(TRACKMOUSEEVENT);
         mTrack.dwFlags = TME_LEAVE;
         mTrack.dwHoverTime = 0;
         mTrack.hwndTrack = mWnd;
         TrackMouseEvent(&mTrack);
@@ -5327,22 +5330,29 @@ bool nsWindow::ProcessMessage(UINT msg, 
           eMouseMove, wParam, lParam, false, WidgetMouseEvent::eLeftButton,
           MOUSE_INPUT_SOURCE(),
           mPointerEvents.GetCachedPointerInfo(msg, wParam));
       if (userMovedMouse) {
         DispatchPendingEvents();
       }
     } break;
 
-    case WM_NCMOUSEMOVE:
-      // If we receive a mouse move event on non-client chrome, make sure and
-      // send an eMouseExitFromWidget event as well.
-      if (mMousePresent && !sIsInMouseCapture)
+    case WM_NCMOUSEMOVE: {
+      LPARAM lParamClient = lParamToClient(lParam);
+      if (WithinDraggableRegion(GET_X_LPARAM(lParamClient),
+                                GET_Y_LPARAM(lParamClient))) {
+        // If we noticed the mouse moving in our draggable region, forward the
+        // message as a normal WM_MOUSEMOVE.
+        SendMessage(mWnd, WM_MOUSEMOVE, wParam, lParamClient);
+      } else if (mMousePresent && !sIsInMouseCapture) {
+        // If we receive a mouse move event on non-client chrome, make sure and
+        // send an eMouseExitFromWidget event as well.
         SendMessage(mWnd, WM_MOUSELEAVE, 0, 0);
-      break;
+      }
+    } break;
 
     case WM_LBUTTONDOWN: {
       result = DispatchMouseEvent(
           eMouseDown, wParam, lParam, false, WidgetMouseEvent::eLeftButton,
           MOUSE_INPUT_SOURCE(),
           mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
     } break;
@@ -5352,16 +5362,17 @@ bool nsWindow::ProcessMessage(UINT msg, 
           eMouseUp, wParam, lParam, false, WidgetMouseEvent::eLeftButton,
           MOUSE_INPUT_SOURCE(),
           mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
     } break;
 
     case WM_MOUSELEAVE: {
       if (!mMousePresent) break;
+      if (mMouseInDraggableArea) break;
       mMousePresent = false;
 
       // Check if the mouse is over the fullscreen transition window, if so
       // clear sLastMouseMovePoint. This way the WM_MOUSEMOVE we get after the
       // transition window disappears will not be ignored, even if the mouse
       // hasn't moved.
       if (mTransitionWnd && WindowAtMouse() == mTransitionWnd) {
         sLastMouseMovePoint = {0};
@@ -6101,34 +6112,39 @@ int32_t nsWindow::ClientMarginHitTestPoi
       testResult = HTCAPTION;
     else if (bottom || left || right)
       testResult = HTBORDER;
   }
 
   if (!sIsInMouseCapture && allowContentOverride) {
     POINT pt = {mx, my};
     ::ScreenToClient(mWnd, &pt);
-    if (pt.x == mCachedHitTestPoint.x && pt.y == mCachedHitTestPoint.y &&
-        TimeStamp::Now() - mCachedHitTestTime <
-            TimeDuration::FromMilliseconds(HITTEST_CACHE_LIFETIME_MS)) {
-      return mCachedHitTestResult;
-    }
-    if (mDraggableRegion.Contains(pt.x, pt.y)) {
+    if (WithinDraggableRegion(pt.x, pt.y)) {
       testResult = HTCAPTION;
     } else {
       testResult = HTCLIENT;
     }
-    mCachedHitTestPoint = pt;
-    mCachedHitTestTime = TimeStamp::Now();
-    mCachedHitTestResult = testResult;
   }
 
   return testResult;
 }
 
+bool nsWindow::WithinDraggableRegion(int32_t clientX, int32_t clientY) {
+  if (clientX == mCachedHitTestPoint.x && clientY == mCachedHitTestPoint.y &&
+      TimeStamp::Now() - mCachedHitTestTime <
+          TimeDuration::FromMilliseconds(HITTEST_CACHE_LIFETIME_MS)) {
+    return mCachedHitTestResult;
+  }
+  mCachedHitTestPoint = {clientX, clientY};
+  mCachedHitTestTime = TimeStamp::Now();
+
+  mCachedHitTestResult = mDraggableRegion.Contains(clientX, clientY);
+  return mCachedHitTestResult;
+}
+
 TimeStamp nsWindow::GetMessageTimeStamp(LONG aEventTime) const {
   CurrentWindowsTimeGetter getCurrentTime(mWnd);
   return TimeConverter().GetTimeStampFromSystemTime(aEventTime, getCurrentTime);
 }
 
 void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode) {
   if (aIsSleepMode == gIsSleepMode) return;
 
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -478,16 +478,17 @@ class nsWindow final : public nsWindowBa
    */
 #ifdef MOZ_XUL
  private:
   void SetWindowTranslucencyInner(nsTransparencyMode aMode);
   nsTransparencyMode GetWindowTranslucencyInner() const {
     return mTransparencyMode;
   }
   void UpdateGlass();
+  bool WithinDraggableRegion(int32_t clientX, int32_t clientY);
 
  protected:
 #endif  // MOZ_XUL
 
   static bool IsAsyncResponseEvent(UINT aMsg, LRESULT& aResult);
   void IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam);
 
   /**
@@ -543,16 +544,17 @@ class nsWindow final : public nsWindowBa
   bool mUnicodeWidget;
   bool mPainting;
   bool mTouchWindow;
   bool mDisplayPanFeedback;
   bool mHideChrome;
   bool mIsRTL;
   bool mFullscreenMode;
   bool mMousePresent;
+  bool mMouseInDraggableArea;
   bool mDestroyCalled;
   bool mOpeningAnimationSuppressed;
   bool mAlwaysOnTop;
   bool mIsEarlyBlankWindow;
   uint32_t mBlurSuppressLevel;
   DWORD_PTR mOldStyle;
   DWORD_PTR mOldExStyle;
   nsNativeDragTarget* mNativeDragTarget;
@@ -654,27 +656,28 @@ class nsWindow final : public nsWindowBa
   bool mMouseTransparent;
 
   // Whether we're in the process of sending a WM_SETTEXT ourselves
   bool mSendingSetText;
 
   // Whether we we're created as a child window (aka ChildWindow) or not.
   bool mIsChildWindow : 1;
 
+  bool mCachedHitTestResult;
+
   // The point in time at which the last paint completed. We use this to avoid
   //  painting too rapidly in response to frequent input events.
   TimeStamp mLastPaintEndTime;
 
   // The location of the window buttons in the window.
   mozilla::Maybe<LayoutDeviceIntRect> mWindowButtonsRect;
 
   // Caching for hit test results
   POINT mCachedHitTestPoint;
   TimeStamp mCachedHitTestTime;
-  int32_t mCachedHitTestResult;
 
   RefPtr<mozilla::widget::WinCompositorWidget> mBasicLayersSurface;
 
   static bool sNeedsToInitMouseWheelSettings;
   static void InitMouseWheelScrollData();
 
   double mSizeConstraintsScale;  // scale in effect when setting constraints