Bug 506815 - Replace MouseTrailer with TrackMouseEvent. r=jimm a=ritu
☠☠ backed out by 023e4f16666b ☠ ☠
authorChris H-C <chutten@mozilla.com>
Fri, 20 Nov 2015 07:42:00 +0100
changeset 305831 9177f28fc28e7b7ab7185553e24b6af5b5796a17
parent 305830 5a1f8e8a63a8757a35964f0201efd3c23583a38c
child 305832 acb2f0d2d7a078e556ef961748caa7127dc3604a
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm, ritu
bugs506815
milestone44.0
Bug 506815 - Replace MouseTrailer with TrackMouseEvent. r=jimm a=ritu MouseTrailer, using a 200ms timer, was used to track whether a pointer was still present over a window. Windows has, since Windows 2000, offered to do this for us via an API called TrackMouseEvent. (It'll also give us hover timings and non-client area versions if we want) I'm all for having Windows do the work for us, and it'll save us from waking up the main thread five times a second.
widget/windows/nsToolkit.cpp
widget/windows/nsToolkit.h
widget/windows/nsWindow.cpp
--- a/widget/windows/nsToolkit.cpp
+++ b/widget/windows/nsToolkit.cpp
@@ -18,44 +18,40 @@
 
 // unknwn.h is needed to build with WIN32_LEAN_AND_MEAN
 #include <unknwn.h>
 
 using namespace mozilla::widget;
 
 nsToolkit* nsToolkit::gToolkit = nullptr;
 HINSTANCE nsToolkit::mDllInstance = 0;
-MouseTrailer*       nsToolkit::gMouseTrailer;
 
 //-------------------------------------------------------------------------
 //
 // constructor
 //
 //-------------------------------------------------------------------------
 nsToolkit::nsToolkit()  
 {
     MOZ_COUNT_CTOR(nsToolkit);
 
 #if defined(MOZ_STATIC_COMPONENT_LIBS)
     nsToolkit::Startup(GetModuleHandle(nullptr));
 #endif
-
-    gMouseTrailer = &mMouseTrailer;
 }
 
 
 //-------------------------------------------------------------------------
 //
 // destructor
 //
 //-------------------------------------------------------------------------
 nsToolkit::~nsToolkit()
 {
     MOZ_COUNT_DTOR(nsToolkit);
-    gMouseTrailer = nullptr;
 }
 
 void
 nsToolkit::Startup(HMODULE hModule)
 {
     nsToolkit::mDllInstance = hModule;
     WinUtils::Initialize();
     nsUXThemeData::Initialize();
@@ -78,131 +74,8 @@ nsToolkit::Shutdown()
 nsToolkit* nsToolkit::GetToolkit()
 {
   if (!gToolkit) {
     gToolkit = new nsToolkit();
   }
 
   return gToolkit;
 }
-
-
-//-------------------------------------------------------------------------
-//
-//
-//-------------------------------------------------------------------------
-MouseTrailer::MouseTrailer() : mMouseTrailerWindow(nullptr), mCaptureWindow(nullptr),
-  mIsInCaptureMode(false), mEnabled(true)
-{
-}
-//-------------------------------------------------------------------------
-//
-//
-//-------------------------------------------------------------------------
-MouseTrailer::~MouseTrailer()
-{
-  DestroyTimer();
-}
-//-------------------------------------------------------------------------
-//
-//
-//-------------------------------------------------------------------------
-void MouseTrailer::SetMouseTrailerWindow(HWND aWnd) 
-{
-  if (mMouseTrailerWindow != aWnd && mTimer) {
-    // Make sure TimerProc is fired at least once for the old window
-    TimerProc(nullptr, nullptr);
-  }
-  mMouseTrailerWindow = aWnd;
-  CreateTimer();
-}
-
-//-------------------------------------------------------------------------
-//
-//
-//-------------------------------------------------------------------------
-void MouseTrailer::SetCaptureWindow(HWND aWnd) 
-{ 
-  mCaptureWindow = aWnd;
-  if (mCaptureWindow) {
-    mIsInCaptureMode = true;
-  }
-}
-
-//-------------------------------------------------------------------------
-//
-//
-//-------------------------------------------------------------------------
-nsresult MouseTrailer::CreateTimer()
-{
-  if (mTimer || !mEnabled) {
-    return NS_OK;
-  } 
-
-  nsresult rv;
-  mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return mTimer->InitWithFuncCallback(TimerProc, nullptr, 200,
-                                      nsITimer::TYPE_REPEATING_SLACK);
-}
-
-//-------------------------------------------------------------------------
-//
-//
-//-------------------------------------------------------------------------
-void MouseTrailer::DestroyTimer()
-{
-  if (mTimer) {
-    mTimer->Cancel();
-    mTimer = nullptr;
-  }
-}
-
-//-------------------------------------------------------------------------
-//
-//
-//-------------------------------------------------------------------------
-void MouseTrailer::TimerProc(nsITimer* aTimer, void* aClosure)
-{
-  MouseTrailer *mtrailer = nsToolkit::gMouseTrailer;
-  NS_ASSERTION(mtrailer, "MouseTrailer still firing after deletion!");
-
-  // Check to see if we are in mouse capture mode,
-  // Once capture ends we could still get back one more timer event.
-  // Capture could end outside our window.
-  // Also, for some reason when the mouse is on the frame it thinks that
-  // it is inside the window that is being captured.
-  if (mtrailer->mCaptureWindow) {
-    if (mtrailer->mCaptureWindow != mtrailer->mMouseTrailerWindow) {
-      return;
-    }
-  } else {
-    if (mtrailer->mIsInCaptureMode) {
-      // mMouseTrailerWindow could be bad from rolling over the frame, so clear 
-      // it if we were capturing and now this is the first timer callback 
-      // since we canceled the capture
-      mtrailer->mMouseTrailerWindow = nullptr;
-      mtrailer->mIsInCaptureMode = false;
-      return;
-    }
-  }
-
-  if (mtrailer->mMouseTrailerWindow && ::IsWindow(mtrailer->mMouseTrailerWindow)) {
-    POINT mp;
-    DWORD pos = ::GetMessagePos();
-    mp.x = GET_X_LPARAM(pos);
-    mp.y = GET_Y_LPARAM(pos);
-    HWND mouseWnd = ::WindowFromPoint(mp);
-    if (mtrailer->mMouseTrailerWindow != mouseWnd) {
-      // Notify someone that a mouse exit happened.
-      PostMessage(mtrailer->mMouseTrailerWindow, WM_MOUSELEAVE, 0, 0);
-
-      // we are out of this window, destroy timer
-      mtrailer->DestroyTimer();
-      mtrailer->mMouseTrailerWindow = nullptr;
-    }
-  } else {
-    mtrailer->DestroyTimer();
-    mtrailer->mMouseTrailerWindow = nullptr;
-  }
-}
-
--- a/widget/windows/nsToolkit.h
+++ b/widget/windows/nsToolkit.h
@@ -16,51 +16,16 @@
 #ifndef GET_X_LPARAM
 #define GET_X_LPARAM(pt) (short(LOWORD(pt)))
 #endif
 #ifndef GET_Y_LPARAM
 #define GET_Y_LPARAM(pt) (short(HIWORD(pt)))
 #endif
 
 /**
- * Makes sure exit/enter mouse messages are always dispatched.
- * In the case where the mouse has exited the outer most window the
- * only way to tell if it has exited is to set a timer and look at the
- * mouse pointer to see if it is within the outer most window.
- */ 
-
-class MouseTrailer 
-{
-public:
-    HWND                  GetMouseTrailerWindow() { return mMouseTrailerWindow; }
-    HWND                  GetCaptureWindow() { return mCaptureWindow; }
-
-    void                  SetMouseTrailerWindow(HWND aWnd);
-    void                  SetCaptureWindow(HWND aWnd);
-    void                  Disable() { mEnabled = false; DestroyTimer(); }
-    void                  Enable() { mEnabled = true; CreateTimer(); }
-    void                  DestroyTimer();
-
-                          MouseTrailer();
-                          ~MouseTrailer();
-private:
-
-    nsresult              CreateTimer();
-
-    static void           TimerProc(nsITimer* aTimer, void* aClosure);
-
-    // Information for mouse enter/exit events
-    HWND                  mMouseTrailerWindow;
-    HWND                  mCaptureWindow;
-    bool                  mIsInCaptureMode;
-    bool                  mEnabled;
-    nsCOMPtr<nsITimer>    mTimer;
-};
-
-/**
  * Wrapper around the thread running the message pump.
  * The toolkit abstraction is necessary because the message pump must
  * execute within the same thread that created the widget under Win32.
  */ 
 
 class nsToolkit
 {
 public:
@@ -68,20 +33,17 @@ public:
 
 private:
     ~nsToolkit();
 
 public:
     static nsToolkit* GetToolkit();
 
     static HINSTANCE mDllInstance;
-    static MouseTrailer *gMouseTrailer;
 
     static void Startup(HMODULE hModule);
     static void Shutdown();
 
 protected:
     static nsToolkit* gToolkit;
-
-    MouseTrailer mMouseTrailer;
 };
 
 #endif  // TOOLKIT_H
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -3375,26 +3375,19 @@ NS_METHOD nsWindow::EnableDragDrop(bool 
  * SECTION: nsIWidget::CaptureMouse
  *
  * Enables/Disables system mouse capture.
  *
  **************************************************************/
 
 NS_METHOD nsWindow::CaptureMouse(bool aCapture)
 {
-  if (!nsToolkit::gMouseTrailer) {
-    NS_ERROR("nsWindow::CaptureMouse called after nsToolkit destroyed");
-    return NS_OK;
-  }
-
   if (aCapture) {
-    nsToolkit::gMouseTrailer->SetCaptureWindow(mWnd);
     ::SetCapture(mWnd);
   } else {
-    nsToolkit::gMouseTrailer->SetCaptureWindow(nullptr);
     ::ReleaseCapture();
   }
   sIsInMouseCapture = aCapture;
   return NS_OK;
 }
 
 /**************************************************************
  *
@@ -4199,23 +4192,18 @@ nsWindow::DispatchMouseEvent(EventMessag
 
   pluginEvent.wParam = wParam;     // plugins NEED raw OS event flags!
   pluginEvent.lParam = lParam;
 
   event.mPluginEvent.Copy(pluginEvent);
 
   // call the event callback
   if (mWidgetListener) {
-    if (nsToolkit::gMouseTrailer)
-      nsToolkit::gMouseTrailer->Disable();
     if (aEventMessage == eMouseMove) {
-      if (nsToolkit::gMouseTrailer && !sIsInMouseCapture) {
-        nsToolkit::gMouseTrailer->SetMouseTrailerWindow(mWnd);
-      }
-      nsIntRect rect;
+      LayoutDeviceIntRect rect;
       GetBounds(rect);
       rect.x = 0;
       rect.y = 0;
 
       if (rect.Contains(LayoutDeviceIntPoint::ToUntyped(event.refPoint))) {
         if (sCurrentWindow == nullptr || sCurrentWindow != this) {
           if ((nullptr != sCurrentWindow) && (!sCurrentWindow->mInDtor)) {
             LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
@@ -4237,19 +4225,16 @@ nsWindow::DispatchMouseEvent(EventMessag
     } else if (aEventMessage == eMouseExitFromWidget) {
       if (sCurrentWindow == this) {
         sCurrentWindow = nullptr;
       }
     }
 
     result = ConvertStatus(DispatchInputEvent(&event));
 
-    if (nsToolkit::gMouseTrailer)
-      nsToolkit::gMouseTrailer->Enable();
-
     // 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;
 }
@@ -5092,16 +5077,25 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       if (!AutoErase((HDC)wParam)) {
         *aRetValue = 1;
         result = true;
       }
       break;
 
     case WM_MOUSEMOVE:
     {
+      if (!mMousePresent) {
+        // First MOOUSEMOVE 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);
+      }
       mMousePresent = true;
 
       // Suppress dispatch of pending events
       // when mouse moves are generated by widget
       // creation instead of user input.
       LPARAM lParamScreen = lParamToScreen(lParam);
       POINT mp;
       mp.x      = GET_X_LPARAM(lParamScreen);
@@ -6686,33 +6680,22 @@ void nsWindow::OnDestroy()
   if (this == rollupWidget) {
     if ( rollupListener )
       rollupListener->Rollup(0, false, nullptr, nullptr);
     CaptureRollupEvents(nullptr, false);
   }
 
   IMEHandler::OnDestroyWindow(this);
 
-  // Turn off mouse trails if enabled.
-  MouseTrailer* mtrailer = nsToolkit::gMouseTrailer;
-  if (mtrailer) {
-    if (mtrailer->GetMouseTrailerWindow() == mWnd)
-      mtrailer->DestroyTimer();
-
-    if (mtrailer->GetCaptureWindow() == mWnd)
-      mtrailer->SetCaptureWindow(nullptr);
-  }
-
   // Free GDI window class objects
   if (mBrush) {
     VERIFY(::DeleteObject(mBrush));
     mBrush = nullptr;
   }
 
-
   // Destroy any custom cursor resources.
   if (mCursor == -1)
     SetCursor(eCursor_standard);
 
 #ifdef MOZ_XUL
   // Reset transparency
   if (eTransparencyTransparent == mTransparencyMode)
     SetupTranslucentWindowMemoryBitmap(eTransparencyOpaque);