Bug 953146 part.7 Don't allow other application to activate non-focusable popup r=jimm
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -7298,16 +7298,35 @@ nsWindow::GetPopupsToRollup(nsIRollupLis
break;
}
}
return true;
}
// static
bool
+nsWindow::NeedsToHandleNCActivateDelayed(HWND aWnd)
+{
+ // While popup is open, popup window might be activated by other application.
+ // At this time, we need to take back focus to the previous window but it
+ // causes flickering its nonclient area because WM_NCACTIVATE comes before
+ // WM_ACTIVATE and we cannot know which window will take focus at receiving
+ // WM_NCACTIVATE. Therefore, we need a hack for preventing the flickerling.
+ //
+ // If non-popup window receives WM_NCACTIVATE at deactivating, default
+ // wndproc shouldn't handle it as deactivating. Instead, at receiving
+ // WM_ACTIVIATE after that, WM_NCACTIVATE should be sent again manually.
+ // This returns true if the window needs to handle WM_NCACTIVATE later.
+
+ nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
+ return window && !window->IsPopup();
+}
+
+// static
+bool
nsWindow::DealWithPopups(HWND aWnd, UINT aMessage,
WPARAM aWParam, LPARAM aLParam, LRESULT* aResult)
{
NS_ASSERTION(aResult, "Bad outResult");
// XXX Why do we use the return value of WM_MOUSEACTIVATE for all messages?
*aResult = MA_NOACTIVATE;
@@ -7318,16 +7337,18 @@ nsWindow::DealWithPopups(HWND aWnd, UINT
nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
NS_ENSURE_TRUE(rollupListener, false);
nsCOMPtr<nsIWidget> popup = rollupListener->GetRollupWidget();
if (!popup) {
return false;
}
+ static bool sSendingNCACTIVATE = false;
+ static bool sPendingNCACTIVATE = false;
uint32_t popupsToRollup = UINT32_MAX;
nsWindow* popupWindow = static_cast<nsWindow*>(popup.get());
UINT nativeMessage = WinUtils::GetNativeMessage(aMessage);
switch (nativeMessage) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
@@ -7352,23 +7373,69 @@ nsWindow::DealWithPopups(HWND aWnd, UINT
}
}
return false;
case WM_ACTIVATEAPP:
break;
case WM_ACTIVATE:
+ // NOTE: Don't handle WA_INACTIVE for preventing popup taking focus
+ // because we cannot distinguish it's caused by mouse or not.
+ if (LOWORD(aWParam) == WA_ACTIVE && aLParam) {
+ nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
+ if (window && window->IsPopup()) {
+ // Cancel notifying widget listeners of deactivating the previous
+ // active window (see WM_KILLFOCUS case in ProcessMessage()).
+ sJustGotDeactivate = false;
+ // Reactivate the window later.
+ ::PostMessageW(aWnd, MOZ_WM_REACTIVATE, aWParam, aLParam);
+ return true;
+ }
+ } else if (sPendingNCACTIVATE && LOWORD(aWParam) == WA_INACTIVE &&
+ NeedsToHandleNCActivateDelayed(aWnd)) {
+ // If focus moves to non-popup widget or focusable popup, the window
+ // needs to update its nonclient area.
+ nsWindow* activeWindow =
+ WinUtils::GetNSWindowPtr(reinterpret_cast<HWND>(aLParam));
+ if (!activeWindow || !activeWindow->IsPopup()) {
+ sSendingNCACTIVATE = true;
+ ::SendMessageW(aWnd, WM_NCACTIVATE, false, 0);
+ sSendingNCACTIVATE = false;
+ }
+ sPendingNCACTIVATE = false;
+ }
// XXX Why do we need to check the message pos?
if (!EventIsInsideWindow(popupWindow) &&
GetPopupsToRollup(rollupListener, &popupsToRollup)) {
break;
}
return false;
+ case MOZ_WM_REACTIVATE:
+ // The previous active window should take back focus.
+ if (::IsWindow(reinterpret_cast<HWND>(aLParam))) {
+ ::SetForegroundWindow(reinterpret_cast<HWND>(aLParam));
+ }
+ return true;
+
+ case WM_NCACTIVATE:
+ if (!aWParam && !sSendingNCACTIVATE &&
+ NeedsToHandleNCActivateDelayed(aWnd)) {
+ // Don't just consume WM_NCACTIVATE. It doesn't handle only the
+ // nonclient area state change.
+ ::DefWindowProcW(aWnd, aMessage, TRUE, aLParam);
+ // Accept the deactivating because it's necessary to receive following
+ // WM_ACTIVATE.
+ *aResult = TRUE;
+ sPendingNCACTIVATE = true;
+ return true;
+ }
+ return false;
+
case WM_MOUSEACTIVATE:
// XXX Why do we need to check the message pos?
if (!EventIsInsideWindow(popupWindow) &&
GetPopupsToRollup(rollupListener, &popupsToRollup)) {
// WM_MOUSEACTIVATE may be caused by moving the mouse (e.g., X-mouse
// of TweakUI is enabled. Then, check if the popup should be rolled up
// with rollup listener. If not, just consume the message.
if (HIWORD(aLParam) == WM_MOUSEMOVE &&
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -416,16 +416,17 @@ protected:
/**
* Popup hooks
*/
static void ScheduleHookTimer(HWND aWnd, UINT aMsgId);
static void RegisterSpecialDropdownHooks();
static void UnregisterSpecialDropdownHooks();
static bool GetPopupsToRollup(nsIRollupListener* aRollupListener,
uint32_t* aPopupsToRollup);
+ static bool NeedsToHandleNCActivateDelayed(HWND aWnd);
static bool DealWithPopups(HWND inWnd, UINT inMsg, WPARAM inWParam, LPARAM inLParam, LRESULT* outResult);
/**
* Window transparency helpers
*/
#ifdef MOZ_XUL
private:
void SetWindowTranslucencyInner(nsTransparencyMode aMode);
--- a/widget/windows/nsWindowDefs.h
+++ b/widget/windows/nsWindowDefs.h
@@ -28,20 +28,23 @@
// Our internal message for WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_VSCROLL and
// WM_HSCROLL
#define MOZ_WM_MOUSEVWHEEL (WM_APP+0x0310)
#define MOZ_WM_MOUSEHWHEEL (WM_APP+0x0311)
#define MOZ_WM_VSCROLL (WM_APP+0x0312)
#define MOZ_WM_HSCROLL (WM_APP+0x0313)
#define MOZ_WM_MOUSEWHEEL_FIRST MOZ_WM_MOUSEVWHEEL
#define MOZ_WM_MOUSEWHEEL_LAST MOZ_WM_HSCROLL
+// If a popup window is being activated, we try to reactivate the previous
+// window with this message.
+#define MOZ_WM_REACTIVATE (WM_APP+0x0314)
// Internal message for ensuring the file picker is visible on multi monitor
// systems, and when the screen resolution changes.
-#define MOZ_WM_ENSUREVISIBLE (WM_APP + 14159)
+#define MOZ_WM_ENSUREVISIBLE (WM_APP+0x374F)
#ifndef SM_CXPADDEDBORDER
#define SM_CXPADDEDBORDER 92
#endif
#ifndef WM_THEMECHANGED
#define WM_THEMECHANGED 0x031A
#endif