Bug 506815 - Replace MouseTrailer with TrackMouseEvent. r=jimm
authorChris H-C <chutten@mozilla.com>
Fri, 20 Nov 2015 07:42:00 +0100
changeset 274205 9e87a89f401dd2e998b456be8bdad5b8ee8db9f6
parent 274204 0288a0a7003ffba272dc8040567e225d7115d9bc
child 274206 5efb9a11196ba113b830cf4717d85513e3468783
push id29727
push usercbook@mozilla.com
push dateThu, 26 Nov 2015 15:54:54 +0000
treeherdermozilla-central@74c7941a9e22 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs506815
milestone45.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 506815 - Replace MouseTrailer with TrackMouseEvent. r=jimm 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
@@ -3376,26 +3376,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;
 }
 
 /**************************************************************
  *
@@ -4195,22 +4188,17 @@ 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);
-      }
       LayoutDeviceIntRect rect;
       GetBounds(rect);
       rect.x = 0;
       rect.y = 0;
 
       if (rect.Contains(event.refPoint)) {
         if (sCurrentWindow == nullptr || sCurrentWindow != this) {
           if ((nullptr != sCurrentWindow) && (!sCurrentWindow->mInDtor)) {
@@ -4233,19 +4221,16 @@ nsWindow::DispatchMouseEvent(EventMessag
     } else if (aEventMessage == eMouseExitFromWidget) {
       if (sCurrentWindow == this) {
         sCurrentWindow = nullptr;
       }
     }
 
     result = ConvertStatus(DispatchAPZAwareEvent(&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;
 }
@@ -5088,16 +5073,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);
@@ -6680,33 +6674,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);