Bug 1835851 - [1/3] signal taskbar after message has been processed r=handyman,gstoll

To work around a race condition involving unminimizing windows, post a
message to our own message queue, to notify the taskbar that fullscreen
state recalculation is potentially necessary. (This notification must
occur _after_ we have finished processing the `WM_WINDOWPOSCHANGING`
message.)

Differential Revision: https://phabricator.services.mozilla.com/D184762
This commit is contained in:
Ray Kraesig 2023-07-28 00:16:46 +00:00
Родитель eb41625b1c
Коммит d9c931ba57
5 изменённых файлов: 80 добавлений и 1 удалений

Просмотреть файл

@ -15564,6 +15564,13 @@
value: true
mirror: always
# Whether to give explorer.exe a delated nudge to recalculate the fullscreenness
# of a window after maximizing it.
- name: widget.windows.fullscreen_remind_taskbar
type: RelaxedAtomicBool
value: true
mirror: always
# The number of messages of each type to keep for display in
# about:windows-messages
- name: widget.windows.messages_to_log

Просмотреть файл

@ -33,10 +33,13 @@
#define MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE (WM_APP + 0x0315)
// Internal message used in correcting backwards clock skew
#define MOZ_WM_SKEWFIX (WM_APP + 0x0316)
// Internal message used for rolling up popups for dmanip events
#define MOZ_WM_DMANIP (WM_APP + 0x0317)
// Internal message used to work around race condition in explorer.exe's
// fullscreen window-state update handler in Windows 10+. (See bug 1835851.)
#define MOZ_WM_FULLSCREEN_STATE_UPDATE (WM_APP + 0x0318)
// XXX Should rename them to MOZ_WM_* and use safer values!
// Messages for fullscreen transition window
#define WM_FULLSCREEN_TRANSITION_BEFORE (WM_USER + 0)

Просмотреть файл

@ -77,6 +77,7 @@
#include <algorithm>
#include <limits>
#include "mozilla/widget/WinMessages.h"
#include "nsWindow.h"
#include "nsWindowTaskbarConcealer.h"
#include "nsAppRunner.h"
@ -5921,6 +5922,12 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam,
result = true;
} break;
// Workaround for race condition in explorer.exe.
case MOZ_WM_FULLSCREEN_STATE_UPDATE: {
TaskbarConcealer::OnAsyncStateUpdateRequest(mWnd);
result = true;
} break;
case WM_GETMINMAXINFO: {
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
// Set the constraints. The minimum size should also be constrained to the
@ -9091,6 +9098,21 @@ void nsWindow::FrameState::EnsureSizeMode(nsSizeMode aMode,
return;
}
if (::IsWin10OrLater() &&
StaticPrefs::widget_windows_fullscreen_remind_taskbar()) {
// If we're unminimizing a window, asynchronously notify the taskbar after
// the message has been processed. This redundant notification works around
// a race condition in explorer.exe. (See bug 1835851, or comments in
// TaskbarConcealer.)
//
// Note that we notify regardless of `aMode`: unminimizing a non-fullscreen
// window can also affect the correct taskbar state, yet fail to affect the
// current taskbar state.
if (mSizeMode == nsSizeMode_Minimized) {
::PostMessage(mWindow->mWnd, MOZ_WM_FULLSCREEN_STATE_UPDATE, 0, 0);
}
}
if (aMode == nsSizeMode_Fullscreen) {
EnsureFullscreenMode(true, aDoShowWindow);
MOZ_ASSERT(mSizeMode == nsSizeMode_Fullscreen);

Просмотреть файл

@ -362,6 +362,50 @@ void nsWindow::TaskbarConcealer::OnWindowPosChanged(nsWindow* aWin) {
UpdateAllState();
}
void nsWindow::TaskbarConcealer::OnAsyncStateUpdateRequest(HWND hwnd) {
MOZ_LOG(sTaskbarConcealerLog, LogLevel::Info,
("==> OnAsyncStateUpdateRequest()"));
// Work around a race condition in explorer.exe.
//
// When a window is unminimized (and on several other events), the taskbar
// receives a notification that it needs to recalculate the current
// is-a-fullscreen-window-active-here-state ("rudeness") of each monitor.
// Unfortunately, this notification is sent concurrently with the
// WM_WINDOWPOSCHANGING message that performs the unminimization.
//
// Until that message is resolved, the window's position is still "minimized".
// If the taskbar processes its notification faster than the window handles
// its WM_WINDOWPOSCHANGING message, then the window will appear to the
// taskbar to still be minimized, and won't be taken into account for
// computing rudeness. This usually presents as a just-unminimized Firefox
// fullscreen-window occasionally having the taskbar stuck above it.
//
// Unfortunately, it's a bit difficult to improve Firefox's speed-of-response
// to WM_WINDOWPOSCHANGING messages (we can, and do, execute JavaScript during
// these), and even if we could that wouldn't always fix it. We instead adopt
// a variant of a strategy by Etienne Duchamps, who has investigated and
// documented this issue extensively[0]: we simply send another signal to the
// shell to notify it to recalculate the current rudeness state of all
// monitors.
//
// [0] https://github.com/dechamps/RudeWindowFixer#a-race-condition-activating-a-minimized-window
//
if (::IsWin10OrLater()) {
static UINT const shellHookMsg = ::RegisterWindowMessageW(L"SHELLHOOK");
if (shellHookMsg != 0) {
// Identifying the particular thread of the particular instance of the
// shell associated with our current desktop is probably possible, but
// also probably not worth the effort. Just broadcast the message
// globally.
DWORD info = BSM_APPLICATIONS;
::BroadcastSystemMessage(BSF_POSTMESSAGE | BSF_IGNORECURRENTTASK, &info,
shellHookMsg, HSHELL_WINDOWACTIVATED,
(LPARAM)hwnd);
}
}
}
void nsWindow::TaskbarConcealer::OnCloakChanged() {
if (!UseAlternateFullscreenHeuristics()) {
return;

Просмотреть файл

@ -35,6 +35,9 @@ class nsWindow::TaskbarConcealer {
// all windows' internal cloaking-state mirror variables are up-to-date.)
static void OnCloakChanged();
// To be called upon receipt of MOZ_WM_FULLSCREEN_STATE_UPDATE.
static void OnAsyncStateUpdateRequest(HWND);
private:
static void UpdateAllState(HWND destroyedHwnd = nullptr);