Bug 782547, allow killfocus and deactivate messages to arrive in any order, remove blur suppression code, r=jmathies
authorNeil Deakin <neil@mozilla.com>
Wed, 24 Oct 2012 16:05:29 -0400
changeset 111415 b6089a8b78d3ab43a075f67a4a4baed96e68f91d
parent 111414 d400b4e402f39f661d5ce2ede318fdcb15839797
child 111416 b02152aa3ed82842390caef6bf392ceb97eb1b01
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersjmathies
bugs782547
milestone19.0a1
Bug 782547, allow killfocus and deactivate messages to arrive in any order, remove blur suppression code, r=jmathies
dom/base/nsGlobalWindow.h
widget/windows/nsFilePicker.cpp
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -820,18 +820,16 @@ protected:
   nsresult SetDocShellWidthAndHeight(int32_t width, int32_t height);
 
   static bool CanSetProperty(const char *aPrefName);
 
   static void MakeScriptDialogTitle(nsAString &aOutTitle);
 
   bool CanMoveResizeWindows();
 
-  bool     GetBlurSuppression();
-
   // If aDoFlush is true, we'll flush our own layout; otherwise we'll try to
   // just flush our parent and only flush ourselves if we think we need to.
   nsresult GetScrollXY(int32_t* aScrollX, int32_t* aScrollY,
                        bool aDoFlush);
   nsresult GetScrollMaxXY(int32_t* aScrollMaxX, int32_t* aScrollMaxY);
 
   nsresult GetOuterSize(nsIntSize* aSizeCSSPixels);
   nsresult SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth);
--- a/widget/windows/nsFilePicker.cpp
+++ b/widget/windows/nsFilePicker.cpp
@@ -39,37 +39,16 @@ static const unsigned long kDialogTimerT
 #define MAX_EXTENSION_LENGTH 10
 #define FILE_BUFFER_SIZE     4096 
 
 typedef DWORD FILEOPENDIALOGOPTIONS;
 
 ///////////////////////////////////////////////////////////////////////////////
 // Helper classes
 
-// Manages matching SuppressBlurEvents calls on the parent widget.
-class AutoSuppressEvents
-{
-public:
-  explicit AutoSuppressEvents(nsIWidget* aWidget) :
-    mWindow(static_cast<nsWindow *>(aWidget)) {
-    SuppressWidgetEvents(true);
-  }
-
-  ~AutoSuppressEvents() {
-    SuppressWidgetEvents(false);
-  }
-private:
-  void SuppressWidgetEvents(bool aFlag) {
-    if (mWindow) {
-      mWindow->SuppressBlurEvents(aFlag);
-    }
-  }
-  nsRefPtr<nsWindow> mWindow;
-};
-
 // Manages the current working path.
 class AutoRestoreWorkingPath
 {
 public:
   AutoRestoreWorkingPath() {
     DWORD bufferLength = GetCurrentDirectoryW(0, NULL);
     mWorkingPath = new PRUnichar[bufferLength];
     if (GetCurrentDirectoryW(bufferLength, mWorkingPath) == 0) {
@@ -1022,18 +1001,16 @@ nsFilePicker::ShowFilePicker(const nsStr
 
 NS_IMETHODIMP
 nsFilePicker::ShowW(int16_t *aReturnVal)
 {
   NS_ENSURE_ARG_POINTER(aReturnVal);
 
   *aReturnVal = returnCancel;
 
-  AutoSuppressEvents supress(mParentWidget);
-
   nsAutoString initialDir;
   if (mDisplayDirectory)
     mDisplayDirectory->GetPath(initialDir);
 
   // If no display directory, re-use the last one.
   if(initialDir.IsEmpty()) {
     // Allocate copy of last used dir.
     initialDir = mLastUsedUnicodeDirectory;
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -159,17 +159,17 @@
 
 #include "nsWindowDefs.h"
 
 #include "nsCrashOnException.h"
 #include "nsIXULRuntime.h"
 
 #include "nsIContent.h"
 
-#include "mozilla/HangMonitor.h"
+#include "mozilla/HangMonitor.h"
 #include "nsIMM32Handler.h"
 
 using namespace mozilla::widget;
 using namespace mozilla::layers;
 using namespace mozilla;
 
 /**************************************************************
  **************************************************************
@@ -338,17 +338,16 @@ nsWindow::nsWindow() : nsBaseWidget()
   mLastPoint.x          = 0;
   mLastPoint.y          = 0;
   mLastSize.width       = 0;
   mLastSize.height      = 0;
   mOldStyle             = 0;
   mOldExStyle           = 0;
   mPainting             = 0;
   mLastKeyboardLayout   = 0;
-  mBlurSuppressLevel    = 0;
   mLastPaintEndTime     = TimeStamp::Now();
 #ifdef MOZ_XUL
   mTransparentSurface   = nullptr;
   mMemoryDC             = nullptr;
   mTransparencyMode     = eTransparencyOpaque;
   memset(&mGlassMargins, 0, sizeof mGlassMargins);
 #endif
   mBackground           = ::GetSysColor(COLOR_BTNFACE);
@@ -4007,45 +4006,48 @@ bool nsWindow::DispatchMouseEvent(uint32
     // the context menu key code in nsEventListenerManager::HandleEvent()
     // released it already.
     return result;
   }
 
   return result;
 }
 
+HWND nsWindow::GetTopLevelForFocus(HWND aCurWnd)
+{
+  // retrieve the toplevel window or dialog
+  HWND toplevelWnd = NULL;
+  while (aCurWnd) {
+    toplevelWnd = aCurWnd;
+
+    nsWindow *win = WinUtils::GetNSWindowPtr(aCurWnd);
+    if (win) {
+      nsWindowType wintype;
+      win->GetWindowType(wintype);
+      if (wintype == eWindowType_toplevel || wintype == eWindowType_dialog)
+        break;
+    }
+
+    aCurWnd = ::GetParent(aCurWnd); // Parent or owner (if has no parent)
+  }
+
+  return toplevelWnd;
+}
+
 void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate)
 {
   if (aIsActivate)
     sJustGotActivate = false;
   sJustGotDeactivate = false;
-
-  if (!aIsActivate && BlurEventsSuppressed())
-    return;
+  mLastKillFocusWindow = NULL;
 
   if (!mWidgetListener)
     return;
 
-  // retrive the toplevel window or dialog
-  HWND curWnd = mWnd;
-  HWND toplevelWnd = NULL;
-  while (curWnd) {
-    toplevelWnd = curWnd;
-
-    nsWindow *win = WinUtils::GetNSWindowPtr(curWnd);
-    if (win) {
-      nsWindowType wintype;
-      win->GetWindowType(wintype);
-      if (wintype == eWindowType_toplevel || wintype == eWindowType_dialog)
-        break;
-    }
-
-    curWnd = ::GetParent(curWnd); // Parent or owner (if has no parent)
-  }
-
+  HWND toplevelWnd = GetTopLevelForFocus(mWnd);
   if (toplevelWnd) {
     nsWindow *win = WinUtils::GetNSWindowPtr(toplevelWnd);
     if (win) {
       if (aIsActivate)
         mWidgetListener->WindowActivated();
       else
         mWidgetListener->WindowDeactivated();
     }
@@ -4065,46 +4067,16 @@ bool nsWindow::IsTopLevelMouseExit(HWND 
   // the non-client area, we should treat it as a top-level exit.
   HWND mouseTopLevel = WinUtils::GetTopLevelHWND(mouseWnd);
   if (mouseWnd == mouseTopLevel)
     return true;
 
   return WinUtils::GetTopLevelHWND(aWnd) != mouseTopLevel;
 }
 
-bool nsWindow::BlurEventsSuppressed()
-{
-  // are they suppressed in this window?
-  if (mBlurSuppressLevel > 0)
-    return true;
-
-  // are they suppressed by any container widget?
-  HWND parentWnd = ::GetParent(mWnd);
-  if (parentWnd) {
-    nsWindow *parent = WinUtils::GetNSWindowPtr(parentWnd);
-    if (parent)
-      return parent->BlurEventsSuppressed();
-  }
-  return false;
-}
-
-// In some circumstances (opening dependent windows) it makes more sense
-// (and fixes a crash bug) to not blur the parent window. Called from
-// nsFilePicker.
-void nsWindow::SuppressBlurEvents(bool aSuppress)
-{
-  if (aSuppress)
-    ++mBlurSuppressLevel; // for this widget
-  else {
-    NS_ASSERTION(mBlurSuppressLevel > 0, "unbalanced blur event suppression");
-    if (mBlurSuppressLevel > 0)
-      --mBlurSuppressLevel;
-  }
-}
-
 bool nsWindow::ConvertStatus(nsEventStatus aStatus)
 {
   return aStatus == nsEventStatus_eConsumeNoDefault;
 }
 
 /**************************************************************
  *
  * SECTION: IPC
@@ -5025,19 +4997,23 @@ bool nsWindow::ProcessMessage(UINT msg, 
     // events are fired. Instead, set either the sJustGotActivate or
     // gJustGotDeactivate flags and activate/deactivate once the focus
     // events arrive.
     case WM_ACTIVATE:
       if (mWidgetListener) {
         int32_t fActive = LOWORD(wParam);
 
         if (WA_INACTIVE == fActive) {
-          // when minimizing a window, the deactivation and focus events will
+          // When minimizing a window, the deactivation and focus events will
           // be fired in the reverse order. Instead, just deactivate right away.
-          if (HIWORD(wParam))
+          // This can also happen when a modal file dialog is opened, so check
+          // if the last window to receive the WM_KILLFOCUS message was this one
+          // or a child of this one.
+          if (HIWORD(wParam) ||
+              (mLastKillFocusWindow && (GetTopLevelForFocus(mLastKillFocusWindow) == mWnd)))
             DispatchFocusToTopLevelWindow(false);
           else
             sJustGotDeactivate = true;
 
           if (mIsTopWidgetWindow)
             mLastKeyboardLayout = gKbdLayout.GetLayout();
 
         } else {
@@ -5103,16 +5079,19 @@ bool nsWindow::ProcessMessage(UINT msg, 
         DispatchFocusToTopLevelWindow(true);
       }
       break;
 
     case WM_KILLFOCUS:
       if (sJustGotDeactivate) {
         DispatchFocusToTopLevelWindow(false);
       }
+      else {
+        mLastKillFocusWindow = mWnd;
+      }
       break;
 
     case WM_WINDOWPOSCHANGED:
     {
       WINDOWPOS *wp = (LPWINDOWPOS)lParam;
       OnWindowPosChanged(wp, result);
     }
     break;
@@ -7076,31 +7055,34 @@ void nsWindow::OnDestroy()
   // Dispatch the destroy notification.
   if (!mInDtor)
     NotifyWindowDestroyed();
 
   // Prevent the widget from sending additional events.
   mWidgetListener = nullptr;
   mAttachedWidgetListener = nullptr;
 
+  if (mWnd == mLastKillFocusWindow)
+    mLastKillFocusWindow = NULL;
+
   // Free our subclass and clear |this| stored in the window props. We will no longer
   // receive events from Windows after this point.
   SubclassWindow(FALSE);
 
   // Once mWidgetListener is cleared and the subclass is reset, sCurrentWindow can be
   // cleared. (It's used in tracking windows for mouse events.)
   if (sCurrentWindow == this)
     sCurrentWindow = nullptr;
 
   // Disconnects us from our parent, will call our GetParent().
   nsBaseWidget::Destroy();
 
   // Release references to children, device context, toolkit, and app shell.
   nsBaseWidget::OnDestroy();
-  
+
   // Clear our native parent handle.
   // XXX Windows will take care of this in the proper order, and SetParent(nullptr)'s
   // remove child on the parent already took place in nsBaseWidget's Destroy call above.
   //SetParent(nullptr);
   mParent = nullptr;
 
   // We have to destroy the native drag target before we null out our window pointer.
   EnableDragDrop(false);
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -198,18 +198,16 @@ public:
   virtual bool            DispatchKeyEvent(nsKeyEvent& aKeyEvent,
                                            const MSG *aMsgSentToPlugin);
   void                    DispatchPendingEvents();
   bool                    DispatchPluginEvent(UINT aMessage,
                                               WPARAM aWParam,
                                               LPARAM aLParam,
                                               bool aDispatchPendingEvents);
 
-  void                    SuppressBlurEvents(bool aSuppress); // Called from nsFilePicker
-  bool                    BlurEventsSuppressed();
 #ifdef ACCESSIBILITY
   Accessible* GetRootAccessible();
 #endif // ACCESSIBILITY
 
   /**
    * Window utilities
    */
   nsWindow*               GetTopLevelWindow(bool aStopOnDialogOrPopup);
@@ -324,16 +322,17 @@ protected:
     return mTransparencyMode == eTransparencyGlass ||
            mTransparencyMode == eTransparencyBorderlessGlass;
   }
 
   /**
    * Event processing helpers
    */
   bool                    DispatchPluginEvent(const MSG &aMsg);
+  HWND                    GetTopLevelForFocus(HWND aCurWnd);
   void                    DispatchFocusToTopLevelWindow(bool aIsActivate);
   bool                    DispatchStandardEvent(uint32_t aMsg);
   bool                    DispatchCommandEvent(uint32_t aEventCommand);
   void                    RelayMouseEvent(UINT aMsg, WPARAM wParam, LPARAM lParam);
   static void             RemoveNextCharMessage(HWND aWnd);
   void                    RemoveMessageAndDispatchPluginEvent(UINT aFirstMsg,
                             UINT aLastMsg,
                             nsFakeCharMessage* aFakeCharMessage = nullptr);
@@ -465,29 +464,29 @@ protected:
   bool                  mPainting;
   bool                  mTouchWindow;
   bool                  mDisplayPanFeedback;
   bool                  mHideChrome;
   bool                  mIsRTL;
   bool                  mFullscreenMode;
   bool                  mMousePresent;
   bool                  mDestroyCalled;
-  uint32_t              mBlurSuppressLevel;
   DWORD_PTR             mOldStyle;
   DWORD_PTR             mOldExStyle;
   InputContext mInputContext;
   nsNativeDragTarget*   mNativeDragTarget;
   HKL                   mLastKeyboardLayout;
   nsSizeMode            mOldSizeMode;
   nsSizeMode            mLastSizeMode;
   WindowHook            mWindowHook;
   DWORD                 mAssumeWheelIsZoomUntil;
   uint32_t              mPickerDisplayCount;
   HICON                 mIconSmall;
   HICON                 mIconBig;
+  HWND                  mLastKillFocusWindow;
   static bool           sDropShadowEnabled;
   static uint32_t       sInstanceCount;
   static TriStateBool   sCanQuit;
   static nsWindow*      sCurrentWindow;
   static BOOL           sIsOleInitialized;
   static HCURSOR        sHCursor;
   static imgIContainer* sCursorImgContainer;
   static bool           sSwitchKeyboardLayout;