зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1915665 - [1/2] Streamline WinEventHub r=win-reviewers,gfx-reviewers,handyman,bradwerth
`WinEventHub` was largely taken from Chromium, where its dynamic connection/disconnection features are presumably more used. We don't have any need for that, and anyway if we ever do it'll be easy to add it alongside the static functionality or even migrate the latter over. Hardwire the relevant message-processing and invocation directly into the hidden window's WNDPROC, rather than providing a registration system. Following the discoveries made in bug 1571516, also remove the additional failure-mode checks added in bug 1852801, reverting to a crash (which can be unified with 1571516 when it shows up). This has, as fallout, a minor functional change to the occlusion tracker: the subfeature prefs mentioned above are now checked at message-receipt time, rather than at construction time. Differential Revision: https://phabricator.services.mozilla.com/D220639
This commit is contained in:
Родитель
00dd01290c
Коммит
b81abb2971
|
@ -36,8 +36,8 @@ class WinWindowOcclusionTrackerInteractiveTest : public ::testing::Test {
|
|||
WinWindowOcclusionTracker::Ensure();
|
||||
EXPECT_NE(nullptr, WinWindowOcclusionTracker::Get());
|
||||
|
||||
WinWindowOcclusionTracker::Get()->EnsureDisplayStatusObserver();
|
||||
WinWindowOcclusionTracker::Get()->EnsureSessionChangeObserver();
|
||||
Preferences::SetBool(PREF_DISPLAY_STATE, true);
|
||||
Preferences::SetBool(PREF_SESSION_LOCK, true);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
|
@ -264,19 +264,19 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenVisibleOcclusion) {
|
|||
|
||||
window->SetExpectation(widget::OcclusionState::OCCLUDED);
|
||||
// Unfortunately, this relies on knowing that NativeWindowOcclusionTracker
|
||||
// uses SessionChangeObserver to listen for WM_WTSSESSION_CHANGE messages, but
|
||||
// uses WinEventWindow to listen for WM_WTSSESSION_CHANGE messages, but
|
||||
// actually locking the screen isn't feasible.
|
||||
DWORD currentSessionId = 0;
|
||||
::ProcessIdToSessionId(::GetCurrentProcessId(), ¤tSessionId);
|
||||
::PostMessage(WinEventHub::Get()->GetWnd(), WM_WTSSESSION_CHANGE,
|
||||
::PostMessage(WinEventWindow::GetHwndForTestingOnly(), WM_WTSSESSION_CHANGE,
|
||||
WTS_SESSION_LOCK, currentSessionId);
|
||||
|
||||
while (window->IsExpectingCall()) {
|
||||
WinWindowOcclusionTracker::Get()->TriggerCalculation();
|
||||
|
||||
MSG msg;
|
||||
bool gotMessage =
|
||||
::PeekMessageW(&msg, WinEventHub::Get()->GetWnd(), 0, 0, PM_REMOVE);
|
||||
bool gotMessage = ::PeekMessageW(
|
||||
&msg, WinEventWindow::GetHwndForTestingOnly(), 0, 0, PM_REMOVE);
|
||||
if (gotMessage) {
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessageW(&msg);
|
||||
|
@ -305,19 +305,19 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenHiddenOcclusion) {
|
|||
window->SetExpectation(widget::OcclusionState::HIDDEN);
|
||||
|
||||
// Unfortunately, this relies on knowing that NativeWindowOcclusionTracker
|
||||
// uses SessionChangeObserver to listen for WM_WTSSESSION_CHANGE messages, but
|
||||
// uses WinEventWindow to listen for WM_WTSSESSION_CHANGE messages, but
|
||||
// actually locking the screen isn't feasible.
|
||||
DWORD currentSessionId = 0;
|
||||
::ProcessIdToSessionId(::GetCurrentProcessId(), ¤tSessionId);
|
||||
PostMessage(WinEventHub::Get()->GetWnd(), WM_WTSSESSION_CHANGE,
|
||||
PostMessage(WinEventWindow::GetHwndForTestingOnly(), WM_WTSSESSION_CHANGE,
|
||||
WTS_SESSION_LOCK, currentSessionId);
|
||||
|
||||
while (window->IsExpectingCall()) {
|
||||
WinWindowOcclusionTracker::Get()->TriggerCalculation();
|
||||
|
||||
MSG msg;
|
||||
bool gotMessage =
|
||||
::PeekMessageW(&msg, WinEventHub::Get()->GetWnd(), 0, 0, PM_REMOVE);
|
||||
bool gotMessage = ::PeekMessageW(
|
||||
&msg, WinEventWindow::GetHwndForTestingOnly(), 0, 0, PM_REMOVE);
|
||||
if (gotMessage) {
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessageW(&msg);
|
||||
|
@ -347,7 +347,7 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenDifferentSession) {
|
|||
// currentSessionId.
|
||||
DWORD currentSessionId = 0;
|
||||
::ProcessIdToSessionId(::GetCurrentProcessId(), ¤tSessionId);
|
||||
::PostMessage(WinEventHub::Get()->GetWnd(), WM_WTSSESSION_CHANGE,
|
||||
::PostMessage(WinEventWindow::GetHwndForTestingOnly(), WM_WTSSESSION_CHANGE,
|
||||
WTS_SESSION_LOCK, currentSessionId + 1);
|
||||
|
||||
window->SetExpectation(widget::OcclusionState::VISIBLE);
|
||||
|
@ -357,8 +357,8 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenDifferentSession) {
|
|||
WinWindowOcclusionTracker::Get()->TriggerCalculation();
|
||||
|
||||
MSG msg;
|
||||
bool gotMessage =
|
||||
::PeekMessageW(&msg, WinEventHub::Get()->GetWnd(), 0, 0, PM_REMOVE);
|
||||
bool gotMessage = ::PeekMessageW(
|
||||
&msg, WinEventWindow::GetHwndForTestingOnly(), 0, 0, PM_REMOVE);
|
||||
if (gotMessage) {
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessageW(&msg);
|
||||
|
|
|
@ -9,215 +9,156 @@
|
|||
#include <wtsapi32.h>
|
||||
|
||||
#include "WinEventObserver.h"
|
||||
#include "WinWindowOcclusionTracker.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsHashtablesFwd.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsWindowDbg.h"
|
||||
#include "nsdefs.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
// borrowed from devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483, by way
|
||||
// of the Chromium sandboxing code's "current_module.h"
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
#define CURRENT_MODULE() reinterpret_cast<HMODULE>(&__ImageBase)
|
||||
|
||||
// N.B.: if and when we eliminate the existing `WindowType::Invisible` hidden
|
||||
// window, we must switch to use of `kClassNameHidden` for the class name. (See
|
||||
// commentary therebeside.)
|
||||
const wchar_t kClassNameHidden2[] = L"MozillaHiddenWindowClass2";
|
||||
|
||||
namespace mozilla::widget {
|
||||
|
||||
LazyLogModule gWinEventObserverLog("WinEventObserver");
|
||||
#define LOG(...) MOZ_LOG(gWinEventObserverLog, LogLevel::Info, (__VA_ARGS__))
|
||||
LazyLogModule gWinEventWindowLog("WinEventWindow");
|
||||
#define OBS_LOG(...) \
|
||||
MOZ_LOG(gWinEventWindowLog, ::mozilla::LogLevel::Info, (__VA_ARGS__))
|
||||
|
||||
// static
|
||||
StaticRefPtr<WinEventHub> WinEventHub::sInstance;
|
||||
namespace {
|
||||
namespace evtwin_details {
|
||||
static HWND sHiddenWindow = nullptr;
|
||||
static bool sHiddenWindowShutdown = false;
|
||||
} // namespace evtwin_details
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
bool WinEventHub::Ensure() {
|
||||
if (sInstance) {
|
||||
return true;
|
||||
/* static */
|
||||
void WinEventWindow::Ensure() {
|
||||
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
|
||||
using namespace evtwin_details;
|
||||
|
||||
if (sHiddenWindow) return;
|
||||
if (sHiddenWindowShutdown) return;
|
||||
|
||||
HMODULE const hSelf = CURRENT_MODULE();
|
||||
WNDCLASSW const wc = {.lpfnWndProc = WinEventWindow::WndProc,
|
||||
.hInstance = hSelf,
|
||||
.lpszClassName = kClassNameHidden2};
|
||||
ATOM const atom = ::RegisterClassW(&wc);
|
||||
if (!atom) {
|
||||
// This is known to be possible when the atom table no longer has free
|
||||
// entries, which unfortunately happens more often than one might expect.
|
||||
// See bug 1571516.
|
||||
auto volatile const err [[maybe_unused]] = ::GetLastError();
|
||||
MOZ_CRASH("could not register broadcast-receiver window-class");
|
||||
}
|
||||
|
||||
LOG("WinEventHub::Ensure()");
|
||||
sHiddenWindow =
|
||||
::CreateWindowW((LPCWSTR)(uintptr_t)atom, L"WinEventWindow", 0, 0, 0, 0,
|
||||
0, nullptr, nullptr, hSelf, nullptr);
|
||||
|
||||
RefPtr<WinEventHub> instance = new WinEventHub();
|
||||
if (!instance->Initialize()) {
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
||||
return false;
|
||||
if (!sHiddenWindow) {
|
||||
MOZ_CRASH("could not create broadcast-receiver window");
|
||||
}
|
||||
sInstance = instance;
|
||||
ClearOnShutdown(&sInstance);
|
||||
return true;
|
||||
|
||||
// It should be harmless to leak this window until destruction -- but other
|
||||
// parts of Gecko may expect all windows to be destroyed, so do that.
|
||||
mozilla::RunOnShutdown([]() {
|
||||
sHiddenWindowShutdown = true;
|
||||
::DestroyWindow(sHiddenWindow);
|
||||
sHiddenWindow = nullptr;
|
||||
});
|
||||
};
|
||||
|
||||
/* static */
|
||||
HWND WinEventWindow::GetHwndForTestingOnly() {
|
||||
return evtwin_details::sHiddenWindow;
|
||||
}
|
||||
|
||||
WinEventHub::WinEventHub() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG("WinEventHub::WinEventHub()");
|
||||
}
|
||||
// Callbacks for individual event types. These are private and internal
|
||||
// implementation details of WinEventWindow.
|
||||
namespace {
|
||||
namespace evtwin_details {
|
||||
|
||||
WinEventHub::~WinEventHub() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mObservers.IsEmpty());
|
||||
LOG("WinEventHub::~WinEventHub()");
|
||||
static void OnSessionChange(WPARAM wParam, LPARAM lParam) {
|
||||
if (wParam == WTS_SESSION_LOCK || wParam == WTS_SESSION_UNLOCK) {
|
||||
Maybe<bool> isCurrentSession;
|
||||
DWORD currentSessionId = 0;
|
||||
if (!::ProcessIdToSessionId(::GetCurrentProcessId(), ¤tSessionId)) {
|
||||
isCurrentSession = Nothing();
|
||||
} else {
|
||||
OBS_LOG(
|
||||
"WinEventWindow OnSessionChange() wParam %zu lParam "
|
||||
"%" PRIdLPTR " currentSessionId %lu",
|
||||
wParam, lParam, currentSessionId);
|
||||
|
||||
if (mHWnd) {
|
||||
::DestroyWindow(mHWnd);
|
||||
mHWnd = nullptr;
|
||||
isCurrentSession = Some(static_cast<DWORD>(lParam) == currentSessionId);
|
||||
}
|
||||
if (auto* wwot = WinWindowOcclusionTracker::Get()) {
|
||||
wwot->OnSessionChange(wParam, isCurrentSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WinEventHub::Initialize() {
|
||||
WNDCLASSW wc;
|
||||
HMODULE hSelf = ::GetModuleHandle(nullptr);
|
||||
static void OnPowerBroadcast(WPARAM wParam, LPARAM lParam) {
|
||||
if (wParam == PBT_POWERSETTINGCHANGE) {
|
||||
POWERBROADCAST_SETTING* setting = (POWERBROADCAST_SETTING*)lParam;
|
||||
|
||||
if (!GetClassInfoW(hSelf, L"MozillaWinEventHubClass", &wc)) {
|
||||
ZeroMemory(&wc, sizeof(WNDCLASSW));
|
||||
wc.hInstance = hSelf;
|
||||
wc.lpfnWndProc = WinEventProc;
|
||||
wc.lpszClassName = L"MozillaWinEventHubClass";
|
||||
RegisterClassW(&wc);
|
||||
}
|
||||
|
||||
mHWnd = ::CreateWindowW(L"MozillaWinEventHubClass", L"WinEventHub", 0, 0, 0,
|
||||
0, 0, nullptr, nullptr, hSelf, nullptr);
|
||||
if (!mHWnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
LRESULT CALLBACK WinEventHub::WinEventProc(HWND aHwnd, UINT aMsg,
|
||||
WPARAM aWParam, LPARAM aLParam) {
|
||||
if (sInstance) {
|
||||
sInstance->ProcessWinEventProc(aHwnd, aMsg, aWParam, aLParam);
|
||||
}
|
||||
return ::DefWindowProc(aHwnd, aMsg, aWParam, aLParam);
|
||||
}
|
||||
|
||||
void WinEventHub::ProcessWinEventProc(HWND aHwnd, UINT aMsg, WPARAM aWParam,
|
||||
LPARAM aLParam) {
|
||||
for (const auto& observer : mObservers) {
|
||||
observer->OnWinEventProc(aHwnd, aMsg, aWParam, aLParam);
|
||||
}
|
||||
}
|
||||
|
||||
void WinEventHub::AddObserver(WinEventObserver* aObserver) {
|
||||
LOG("WinEventHub::AddObserver() aObserver %p", aObserver);
|
||||
|
||||
mObservers.Insert(aObserver);
|
||||
}
|
||||
|
||||
void WinEventHub::RemoveObserver(WinEventObserver* aObserver) {
|
||||
LOG("WinEventHub::RemoveObserver() aObserver %p", aObserver);
|
||||
|
||||
mObservers.Remove(aObserver);
|
||||
}
|
||||
|
||||
WinEventObserver::~WinEventObserver() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mDestroyed);
|
||||
}
|
||||
|
||||
void WinEventObserver::Destroy() {
|
||||
LOG("WinEventObserver::Destroy() this %p", this);
|
||||
|
||||
WinEventHub::Get()->RemoveObserver(this);
|
||||
mDestroyed = true;
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<DisplayStatusObserver> DisplayStatusObserver::Create(
|
||||
DisplayStatusListener* aListener) {
|
||||
if (!WinEventHub::Ensure()) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<DisplayStatusObserver> observer = new DisplayStatusObserver(aListener);
|
||||
WinEventHub::Get()->AddObserver(observer);
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
DisplayStatusObserver::DisplayStatusObserver(DisplayStatusListener* aListener)
|
||||
: mListener(aListener) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG("DisplayStatusObserver::DisplayStatusObserver() this %p", this);
|
||||
|
||||
mDisplayStatusHandle = ::RegisterPowerSettingNotification(
|
||||
WinEventHub::Get()->GetWnd(), &GUID_SESSION_DISPLAY_STATUS,
|
||||
DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
}
|
||||
|
||||
DisplayStatusObserver::~DisplayStatusObserver() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG("DisplayStatusObserver::~DisplayStatusObserver() this %p", this);
|
||||
|
||||
if (mDisplayStatusHandle) {
|
||||
::UnregisterPowerSettingNotification(mDisplayStatusHandle);
|
||||
mDisplayStatusHandle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayStatusObserver::OnWinEventProc(HWND aHwnd, UINT aMsg,
|
||||
WPARAM aWParam, LPARAM aLParam) {
|
||||
if (aMsg == WM_POWERBROADCAST && aWParam == PBT_POWERSETTINGCHANGE) {
|
||||
POWERBROADCAST_SETTING* setting = (POWERBROADCAST_SETTING*)aLParam;
|
||||
if (setting &&
|
||||
::IsEqualGUID(setting->PowerSetting, GUID_SESSION_DISPLAY_STATUS) &&
|
||||
setting->DataLength == sizeof(DWORD)) {
|
||||
bool displayOn = PowerMonitorOff !=
|
||||
static_cast<MONITOR_DISPLAY_STATE>(setting->Data[0]);
|
||||
|
||||
LOG("DisplayStatusObserver::OnWinEventProc() displayOn %d this %p",
|
||||
displayOn, this);
|
||||
mListener->OnDisplayStateChanged(displayOn);
|
||||
OBS_LOG("WinEventWindow OnPowerBroadcast() displayOn %d", displayOn);
|
||||
|
||||
if (auto* wwot = WinWindowOcclusionTracker::Get()) {
|
||||
wwot->OnDisplayStateChanged(displayOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace evtwin_details
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
already_AddRefed<SessionChangeObserver> SessionChangeObserver::Create(
|
||||
SessionChangeListener* aListener) {
|
||||
if (!WinEventHub::Ensure()) {
|
||||
return nullptr;
|
||||
LRESULT CALLBACK WinEventWindow::WndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
NativeEventLogger eventLogger("WinEventWindow", hwnd, msg, wParam, lParam);
|
||||
|
||||
switch (msg) {
|
||||
case WM_WINDOWPOSCHANGING: {
|
||||
// prevent rude external programs from making hidden window visible
|
||||
LPWINDOWPOS info = (LPWINDOWPOS)lParam;
|
||||
info->flags &= ~SWP_SHOWWINDOW;
|
||||
} break;
|
||||
|
||||
case WM_WTSSESSION_CHANGE: {
|
||||
evtwin_details::OnSessionChange(wParam, lParam);
|
||||
} break;
|
||||
|
||||
case WM_POWERBROADCAST: {
|
||||
evtwin_details::OnPowerBroadcast(wParam, lParam);
|
||||
} break;
|
||||
}
|
||||
RefPtr<SessionChangeObserver> observer = new SessionChangeObserver(aListener);
|
||||
WinEventHub::Get()->AddObserver(observer);
|
||||
return observer.forget();
|
||||
|
||||
LRESULT ret = ::DefWindowProcW(hwnd, msg, wParam, lParam);
|
||||
eventLogger.SetResult(ret, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SessionChangeObserver::SessionChangeObserver(SessionChangeListener* aListener)
|
||||
: mListener(aListener) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG("SessionChangeObserver::SessionChangeObserver() this %p", this);
|
||||
|
||||
auto hwnd = WinEventHub::Get()->GetWnd();
|
||||
DebugOnly<BOOL> wtsRegistered =
|
||||
::WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_THIS_SESSION);
|
||||
NS_ASSERTION(wtsRegistered, "WTSRegisterSessionNotification failed!\n");
|
||||
}
|
||||
SessionChangeObserver::~SessionChangeObserver() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG("SessionChangeObserver::~SessionChangeObserver() this %p", this);
|
||||
|
||||
auto hwnd = WinEventHub::Get()->GetWnd();
|
||||
// Unregister notifications from terminal services
|
||||
::WTSUnRegisterSessionNotification(hwnd);
|
||||
}
|
||||
|
||||
void SessionChangeObserver::OnWinEventProc(HWND aHwnd, UINT aMsg,
|
||||
WPARAM aWParam, LPARAM aLParam) {
|
||||
if (aMsg == WM_WTSSESSION_CHANGE &&
|
||||
(aWParam == WTS_SESSION_LOCK || aWParam == WTS_SESSION_UNLOCK)) {
|
||||
Maybe<bool> isCurrentSession;
|
||||
DWORD currentSessionId = 0;
|
||||
if (!::ProcessIdToSessionId(::GetCurrentProcessId(), ¤tSessionId)) {
|
||||
isCurrentSession = Nothing();
|
||||
} else {
|
||||
LOG("SessionChangeObserver::OnWinEventProc() aWParam %zu aLParam "
|
||||
"%" PRIdLPTR
|
||||
" "
|
||||
"currentSessionId %lu this %p",
|
||||
aWParam, aLParam, currentSessionId, this);
|
||||
|
||||
isCurrentSession = Some(static_cast<DWORD>(aLParam) == currentSessionId);
|
||||
}
|
||||
mListener->OnSessionChange(aWParam, isCurrentSession);
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOG
|
||||
#undef OBS_LOG
|
||||
|
||||
} // namespace mozilla::widget
|
||||
|
|
|
@ -7,109 +7,27 @@
|
|||
#ifndef widget_windows_WinEventObserver_h
|
||||
#define widget_windows_WinEventObserver_h
|
||||
|
||||
#include <windows.h>
|
||||
#include <windef.h>
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsTHashSet.h"
|
||||
namespace mozilla::widget {
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace widget {
|
||||
|
||||
class WinEventObserver {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WinEventObserver)
|
||||
class WinEventWindow {
|
||||
public:
|
||||
virtual void OnWinEventProc(HWND aHwnd, UINT aMsg, WPARAM aWParam,
|
||||
LPARAM aLParam) {}
|
||||
virtual void Destroy();
|
||||
// Create the hidden window. This window will persist for the lifetime of the
|
||||
// process: we do not destroy it, but neither does it keep the process alive.
|
||||
//
|
||||
// Must be called in the parent process and on the main thread. Crashes on
|
||||
// failure.
|
||||
static void Ensure();
|
||||
|
||||
protected:
|
||||
virtual ~WinEventObserver();
|
||||
|
||||
bool mDestroyed = false;
|
||||
};
|
||||
|
||||
// Uses singleton window to observe events like display status and session
|
||||
// change.
|
||||
class WinEventHub final {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WinEventHub)
|
||||
|
||||
public:
|
||||
// Returns true if the singleton exists (or was created). Will return
|
||||
// false if the singleton couldn't be created, in which case a call
|
||||
// to Get() will return a nullptr. It is safe to call this function
|
||||
// repeatedly.
|
||||
static bool Ensure();
|
||||
static RefPtr<WinEventHub> Get() { return sInstance; }
|
||||
|
||||
void AddObserver(WinEventObserver* aObserver);
|
||||
void RemoveObserver(WinEventObserver* aObserver);
|
||||
|
||||
HWND GetWnd() { return mHWnd; }
|
||||
// (Do not call in real code.)
|
||||
static HWND GetHwndForTestingOnly();
|
||||
|
||||
private:
|
||||
WinEventHub();
|
||||
~WinEventHub();
|
||||
|
||||
static LRESULT CALLBACK WinEventProc(HWND aHwnd, UINT aMsg, WPARAM aWParam,
|
||||
LPARAM aLParam);
|
||||
|
||||
bool Initialize();
|
||||
void ProcessWinEventProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
HWND mHWnd = nullptr;
|
||||
nsTHashSet<nsRefPtrHashKey<WinEventObserver>> mObservers;
|
||||
|
||||
static StaticRefPtr<WinEventHub> sInstance;
|
||||
// the hidden window's WNDPROC
|
||||
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
|
||||
};
|
||||
|
||||
class DisplayStatusListener {
|
||||
public:
|
||||
virtual void OnDisplayStateChanged(bool aDisplayOn) = 0;
|
||||
};
|
||||
|
||||
// Observe Display on/off event
|
||||
class DisplayStatusObserver final : public WinEventObserver {
|
||||
public:
|
||||
static already_AddRefed<DisplayStatusObserver> Create(
|
||||
DisplayStatusListener* aListener);
|
||||
|
||||
private:
|
||||
explicit DisplayStatusObserver(DisplayStatusListener* aListener);
|
||||
virtual ~DisplayStatusObserver();
|
||||
void OnWinEventProc(HWND aHwnd, UINT aMsg, WPARAM aWParam,
|
||||
LPARAM aLParam) override;
|
||||
|
||||
DisplayStatusListener* mListener;
|
||||
|
||||
HPOWERNOTIFY mDisplayStatusHandle = nullptr;
|
||||
};
|
||||
|
||||
class SessionChangeListener {
|
||||
public:
|
||||
virtual void OnSessionChange(WPARAM aStatusCode,
|
||||
Maybe<bool> aIsCurrentSession) = 0;
|
||||
};
|
||||
|
||||
// Observe session lock/unlock event
|
||||
class SessionChangeObserver : public WinEventObserver {
|
||||
public:
|
||||
static already_AddRefed<SessionChangeObserver> Create(
|
||||
SessionChangeListener* aListener);
|
||||
|
||||
private:
|
||||
explicit SessionChangeObserver(SessionChangeListener* aListener);
|
||||
virtual ~SessionChangeObserver();
|
||||
|
||||
void Initialize();
|
||||
void OnWinEventProc(HWND aHwnd, UINT aMsg, WPARAM aWParam,
|
||||
LPARAM aLParam) override;
|
||||
|
||||
SessionChangeListener* mListener;
|
||||
};
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::widget
|
||||
|
||||
#endif // widget_windows_WinEventObserver_h
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "nsBaseWidget.h"
|
||||
#include "nsWindow.h"
|
||||
#include "transport/runnable_utils.h"
|
||||
#include "WinEventObserver.h"
|
||||
#include "WinUtils.h"
|
||||
|
||||
namespace mozilla::widget {
|
||||
|
@ -341,12 +342,6 @@ void WinWindowOcclusionTracker::Ensure() {
|
|||
if (sTracker->mThread->StartWithOptions(options)) {
|
||||
// Success!
|
||||
sTracker->mHasAttemptedShutdown = false;
|
||||
|
||||
// Take this opportunity to ensure that mDisplayStatusObserver and
|
||||
// mSessionChangeObserver exist. They might have failed to be
|
||||
// created when sTracker was created.
|
||||
sTracker->EnsureDisplayStatusObserver();
|
||||
sTracker->EnsureSessionChangeObserver();
|
||||
return;
|
||||
}
|
||||
// Restart failed, so null out our sTracker and try again with a new
|
||||
|
@ -428,14 +423,6 @@ void WinWindowOcclusionTracker::ShutDown() {
|
|||
}
|
||||
|
||||
void WinWindowOcclusionTracker::Destroy() {
|
||||
if (mDisplayStatusObserver) {
|
||||
mDisplayStatusObserver->Destroy();
|
||||
mDisplayStatusObserver = nullptr;
|
||||
}
|
||||
if (mSessionChangeObserver) {
|
||||
mSessionChangeObserver->Destroy();
|
||||
mSessionChangeObserver = nullptr;
|
||||
}
|
||||
if (mSerializedTaskDispatcher) {
|
||||
mSerializedTaskDispatcher->Destroy();
|
||||
}
|
||||
|
@ -452,26 +439,6 @@ bool WinWindowOcclusionTracker::IsInWinWindowOcclusionThread() {
|
|||
sTracker->mThread->thread_id() == PlatformThread::CurrentId();
|
||||
}
|
||||
|
||||
void WinWindowOcclusionTracker::EnsureDisplayStatusObserver() {
|
||||
if (mDisplayStatusObserver) {
|
||||
return;
|
||||
}
|
||||
if (StaticPrefs::
|
||||
widget_windows_window_occlusion_tracking_display_state_enabled()) {
|
||||
mDisplayStatusObserver = DisplayStatusObserver::Create(this);
|
||||
}
|
||||
}
|
||||
|
||||
void WinWindowOcclusionTracker::EnsureSessionChangeObserver() {
|
||||
if (mSessionChangeObserver) {
|
||||
return;
|
||||
}
|
||||
if (StaticPrefs::
|
||||
widget_windows_window_occlusion_tracking_session_lock_enabled()) {
|
||||
mSessionChangeObserver = SessionChangeObserver::Create(this);
|
||||
}
|
||||
}
|
||||
|
||||
void WinWindowOcclusionTracker::Enable(nsBaseWidget* aWindow, HWND aHwnd) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG(LogLevel::Info, "WinWindowOcclusionTracker::Enable() aWindow %p aHwnd %p",
|
||||
|
@ -533,8 +500,7 @@ WinWindowOcclusionTracker::WinWindowOcclusionTracker(
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG(LogLevel::Info, "WinWindowOcclusionTracker::WinWindowOcclusionTracker()");
|
||||
|
||||
EnsureDisplayStatusObserver();
|
||||
EnsureSessionChangeObserver();
|
||||
WinEventWindow::Ensure();
|
||||
|
||||
mSerializedTaskDispatcher = new SerializedTaskDispatcher();
|
||||
}
|
||||
|
@ -740,6 +706,10 @@ void WinWindowOcclusionTracker::UpdateOcclusionState(
|
|||
void WinWindowOcclusionTracker::OnSessionChange(WPARAM aStatusCode,
|
||||
Maybe<bool> aIsCurrentSession) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!StaticPrefs::
|
||||
widget_windows_window_occlusion_tracking_session_lock_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aIsCurrentSession.isNothing() || !*aIsCurrentSession) {
|
||||
return;
|
||||
|
@ -763,6 +733,11 @@ void WinWindowOcclusionTracker::OnSessionChange(WPARAM aStatusCode,
|
|||
|
||||
void WinWindowOcclusionTracker::OnDisplayStateChanged(bool aDisplayOn) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!StaticPrefs::
|
||||
widget_windows_window_occlusion_tracking_display_state_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(LogLevel::Info,
|
||||
"WinWindowOcclusionTracker::OnDisplayStateChanged() aDisplayOn %d",
|
||||
aDisplayOn);
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
#ifndef widget_windows_WinWindowOcclusionTracker_h
|
||||
#define widget_windows_WinWindowOcclusionTracker_h
|
||||
|
||||
#include <string>
|
||||
#include <windef.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
@ -15,10 +16,7 @@
|
|||
#include "nsIWeakReferenceUtils.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#include "mozilla/ThreadSafeWeakPtr.h"
|
||||
#include "mozilla/widget/WindowOcclusionState.h"
|
||||
#include "mozilla/widget/WinEventObserver.h"
|
||||
#include "Units.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
|
@ -41,8 +39,7 @@ class UpdateOcclusionStateRunnable;
|
|||
|
||||
// This class handles window occlusion tracking by using HWND.
|
||||
// Implementation is borrowed from chromium's NativeWindowOcclusionTrackerWin.
|
||||
class WinWindowOcclusionTracker final : public DisplayStatusListener,
|
||||
public SessionChangeListener {
|
||||
class WinWindowOcclusionTracker final {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WinWindowOcclusionTracker)
|
||||
|
||||
|
@ -61,12 +58,6 @@ class WinWindowOcclusionTracker final : public DisplayStatusListener,
|
|||
/// Can be called from any thread.
|
||||
static bool IsInWinWindowOcclusionThread();
|
||||
|
||||
/// Can only be called from the main thread.
|
||||
void EnsureDisplayStatusObserver();
|
||||
|
||||
/// Can only be called from the main thread.
|
||||
void EnsureSessionChangeObserver();
|
||||
|
||||
// Enables notifying to widget via NotifyOcclusionState() when the occlusion
|
||||
// state has been computed.
|
||||
void Enable(nsBaseWidget* aWindow, HWND aHwnd);
|
||||
|
@ -280,15 +271,16 @@ class WinWindowOcclusionTracker final : public DisplayStatusListener,
|
|||
void UpdateOcclusionState(std::unordered_map<HWND, OcclusionState>* aMap,
|
||||
bool aShowAllWindows);
|
||||
|
||||
public:
|
||||
// This is called with session changed notifications. If the screen is locked
|
||||
// by the current session, it marks app windows as occluded.
|
||||
void OnSessionChange(WPARAM aStatusCode,
|
||||
Maybe<bool> aIsCurrentSession) override;
|
||||
void OnSessionChange(WPARAM aStatusCode, Maybe<bool> aIsCurrentSession);
|
||||
|
||||
// This is called when the display is put to sleep. If the display is sleeping
|
||||
// it marks app windows as occluded.
|
||||
void OnDisplayStateChanged(bool aDisplayOn) override;
|
||||
void OnDisplayStateChanged(bool aDisplayOn);
|
||||
|
||||
private:
|
||||
// Marks all root windows as either occluded, or if hwnd IsIconic, hidden.
|
||||
void MarkNonIconicWindowsOccluded();
|
||||
|
||||
|
@ -316,10 +308,6 @@ class WinWindowOcclusionTracker final : public DisplayStatusListener,
|
|||
// If the display is off, windows are considered occluded.
|
||||
bool mDisplayOn = true;
|
||||
|
||||
RefPtr<DisplayStatusObserver> mDisplayStatusObserver;
|
||||
|
||||
RefPtr<SessionChangeObserver> mSessionChangeObserver;
|
||||
|
||||
// Used to serialize tasks related to mRootWindowHwndsOcclusionState.
|
||||
RefPtr<SerializedTaskDispatcher> mSerializedTaskDispatcher;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче