зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 2 changesets (bug 1915665) for causing failures at WinEventObserver.cpp. CLOSED TREE
Backed out changeset a3d4ad8b9ad8 (bug 1915665) Backed out changeset 1428595baef7 (bug 1915665)
This commit is contained in:
Родитель
c8281fa1c6
Коммит
171a4c7e85
|
@ -36,8 +36,8 @@ class WinWindowOcclusionTrackerInteractiveTest : public ::testing::Test {
|
|||
WinWindowOcclusionTracker::Ensure();
|
||||
EXPECT_NE(nullptr, WinWindowOcclusionTracker::Get());
|
||||
|
||||
Preferences::SetBool(PREF_DISPLAY_STATE, true);
|
||||
Preferences::SetBool(PREF_SESSION_LOCK, true);
|
||||
WinWindowOcclusionTracker::Get()->EnsureDisplayStatusObserver();
|
||||
WinWindowOcclusionTracker::Get()->EnsureSessionChangeObserver();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
|
@ -264,19 +264,19 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenVisibleOcclusion) {
|
|||
|
||||
window->SetExpectation(widget::OcclusionState::OCCLUDED);
|
||||
// Unfortunately, this relies on knowing that NativeWindowOcclusionTracker
|
||||
// uses WinEventWindow to listen for WM_WTSSESSION_CHANGE messages, but
|
||||
// uses SessionChangeObserver to listen for WM_WTSSESSION_CHANGE messages, but
|
||||
// actually locking the screen isn't feasible.
|
||||
DWORD currentSessionId = 0;
|
||||
::ProcessIdToSessionId(::GetCurrentProcessId(), ¤tSessionId);
|
||||
::PostMessage(WinEventWindow::GetHwndForTestingOnly(), WM_WTSSESSION_CHANGE,
|
||||
::PostMessage(WinEventHub::Get()->GetWnd(), WM_WTSSESSION_CHANGE,
|
||||
WTS_SESSION_LOCK, currentSessionId);
|
||||
|
||||
while (window->IsExpectingCall()) {
|
||||
WinWindowOcclusionTracker::Get()->TriggerCalculation();
|
||||
|
||||
MSG msg;
|
||||
bool gotMessage = ::PeekMessageW(
|
||||
&msg, WinEventWindow::GetHwndForTestingOnly(), 0, 0, PM_REMOVE);
|
||||
bool gotMessage =
|
||||
::PeekMessageW(&msg, WinEventHub::Get()->GetWnd(), 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 WinEventWindow to listen for WM_WTSSESSION_CHANGE messages, but
|
||||
// uses SessionChangeObserver to listen for WM_WTSSESSION_CHANGE messages, but
|
||||
// actually locking the screen isn't feasible.
|
||||
DWORD currentSessionId = 0;
|
||||
::ProcessIdToSessionId(::GetCurrentProcessId(), ¤tSessionId);
|
||||
PostMessage(WinEventWindow::GetHwndForTestingOnly(), WM_WTSSESSION_CHANGE,
|
||||
PostMessage(WinEventHub::Get()->GetWnd(), WM_WTSSESSION_CHANGE,
|
||||
WTS_SESSION_LOCK, currentSessionId);
|
||||
|
||||
while (window->IsExpectingCall()) {
|
||||
WinWindowOcclusionTracker::Get()->TriggerCalculation();
|
||||
|
||||
MSG msg;
|
||||
bool gotMessage = ::PeekMessageW(
|
||||
&msg, WinEventWindow::GetHwndForTestingOnly(), 0, 0, PM_REMOVE);
|
||||
bool gotMessage =
|
||||
::PeekMessageW(&msg, WinEventHub::Get()->GetWnd(), 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(WinEventWindow::GetHwndForTestingOnly(), WM_WTSSESSION_CHANGE,
|
||||
::PostMessage(WinEventHub::Get()->GetWnd(), 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, WinEventWindow::GetHwndForTestingOnly(), 0, 0, PM_REMOVE);
|
||||
bool gotMessage =
|
||||
::PeekMessageW(&msg, WinEventHub::Get()->GetWnd(), 0, 0, PM_REMOVE);
|
||||
if (gotMessage) {
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessageW(&msg);
|
||||
|
|
|
@ -4,171 +4,220 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <corecrt_memcpy_s.h>
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
#include <winuser.h>
|
||||
#include <wtsapi32.h>
|
||||
|
||||
#include "WinEventObserver.h"
|
||||
#include "WinWindowOcclusionTracker.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsWindowDbg.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsHashtablesFwd.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 gWinEventWindowLog("WinEventWindow");
|
||||
#define OBS_LOG(...) \
|
||||
MOZ_LOG(gWinEventWindowLog, ::mozilla::LogLevel::Info, (__VA_ARGS__))
|
||||
|
||||
namespace {
|
||||
static HWND sHiddenWindow = nullptr;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void WinEventWindow::Ensure() {
|
||||
if (sHiddenWindow) return;
|
||||
|
||||
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
sHiddenWindow =
|
||||
::CreateWindowW((LPCWSTR)(uintptr_t)atom, L"WinEventWindow", 0, 0, 0, 0,
|
||||
0, nullptr, nullptr, hSelf, nullptr);
|
||||
|
||||
if (!sHiddenWindow) {
|
||||
MOZ_CRASH("could not create broadcast-receiver window");
|
||||
}
|
||||
};
|
||||
|
||||
/* static */
|
||||
HWND WinEventWindow::GetHwndForTestingOnly() { return sHiddenWindow; }
|
||||
|
||||
// Callbacks for individual event types. These are private and internal
|
||||
// implementation details of WinEventWindow.
|
||||
namespace {
|
||||
namespace evtwin_details {
|
||||
|
||||
static void OnSessionChange(WPARAM wParam, LPARAM lParam) {
|
||||
if (wParam == WTS_SESSION_LOCK || wParam == WTS_SESSION_UNLOCK) {
|
||||
DWORD currentSessionId;
|
||||
BOOL const rv =
|
||||
::ProcessIdToSessionId(::GetCurrentProcessId(), ¤tSessionId);
|
||||
if (rv) {
|
||||
// A process should always have the relevant access privileges for itself,
|
||||
// but the above call could still fail if, e.g., someone's playing games
|
||||
// with function imports. If so, just assert and/or skip out.
|
||||
//
|
||||
// Should this turn out to somehow be a real concern, we could do
|
||||
// ```
|
||||
// DWORD const currentSessionId =
|
||||
// ::NtCurrentTeb()->ProcessEnvironmentBlock->SessionId;
|
||||
// ```
|
||||
// instead, which is actually documented (albeit abjured against).
|
||||
MOZ_ASSERT(false, "::ProcessIdToSessionId() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
OBS_LOG("WinEventWindow OnSessionChange(): wParam=%zu lParam=%" PRIdLPTR
|
||||
" currentSessionId=%lu",
|
||||
wParam, lParam, currentSessionId);
|
||||
|
||||
// Ignore lock/unlock messages for other sessions -- which Windows actually
|
||||
// _does_ send in some scenarios; see review of Chromium changeset 1929489:
|
||||
//
|
||||
// https://chromium-review.googlesource.com/c/chromium/src/+/1929489
|
||||
if (currentSessionId != (DWORD)lParam) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* wwot = WinWindowOcclusionTracker::Get()) {
|
||||
wwot->OnSessionChange(wParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnPowerBroadcast(WPARAM wParam, LPARAM lParam) {
|
||||
if (wParam == PBT_POWERSETTINGCHANGE) {
|
||||
POWERBROADCAST_SETTING* setting = (POWERBROADCAST_SETTING*)lParam;
|
||||
MOZ_ASSERT(setting);
|
||||
|
||||
if (::IsEqualGUID(setting->PowerSetting, GUID_SESSION_DISPLAY_STATUS) &&
|
||||
setting->DataLength == sizeof(DWORD)) {
|
||||
MONITOR_DISPLAY_STATE state{};
|
||||
errno_t const err =
|
||||
::memcpy_s(&state, sizeof(state), setting->Data, setting->DataLength);
|
||||
if (err) {
|
||||
MOZ_ASSERT(false, "bad data in POWERBROADCAST_SETTING in lParam");
|
||||
return;
|
||||
}
|
||||
|
||||
bool const displayOn = MONITOR_DISPLAY_STATE::PowerMonitorOff != state;
|
||||
|
||||
OBS_LOG("WinEventWindow OnPowerBroadcast(): displayOn=%d",
|
||||
int(displayOn ? 1 : 0));
|
||||
|
||||
if (auto* wwot = WinWindowOcclusionTracker::Get()) {
|
||||
wwot->OnDisplayStateChanged(displayOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace evtwin_details
|
||||
} // namespace
|
||||
LazyLogModule gWinEventObserverLog("WinEventObserver");
|
||||
#define LOG(...) MOZ_LOG(gWinEventObserverLog, LogLevel::Info, (__VA_ARGS__))
|
||||
|
||||
// static
|
||||
LRESULT CALLBACK WinEventWindow::WndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
NativeEventLogger eventLogger("WinEventWindow", hwnd, msg, wParam, lParam);
|
||||
StaticRefPtr<WinEventHub> WinEventHub::sInstance;
|
||||
|
||||
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;
|
||||
// static
|
||||
bool WinEventHub::Ensure() {
|
||||
if (sInstance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LRESULT ret = ::DefWindowProcW(hwnd, msg, wParam, lParam);
|
||||
eventLogger.SetResult(ret, false);
|
||||
return ret;
|
||||
LOG("WinEventHub::Ensure()");
|
||||
|
||||
RefPtr<WinEventHub> instance = new WinEventHub();
|
||||
if (!instance->Initialize()) {
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
||||
return false;
|
||||
}
|
||||
sInstance = instance;
|
||||
ClearOnShutdown(&sInstance);
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef OBS_LOG
|
||||
WinEventHub::WinEventHub() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG("WinEventHub::WinEventHub()");
|
||||
}
|
||||
|
||||
WinEventHub::~WinEventHub() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mObservers.IsEmpty());
|
||||
LOG("WinEventHub::~WinEventHub()");
|
||||
|
||||
if (mHWnd) {
|
||||
::DestroyWindow(mHWnd);
|
||||
mHWnd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool WinEventHub::Initialize() {
|
||||
WNDCLASSW wc;
|
||||
HMODULE hSelf = ::GetModuleHandle(nullptr);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<SessionChangeObserver> SessionChangeObserver::Create(
|
||||
SessionChangeListener* aListener) {
|
||||
if (!WinEventHub::Ensure()) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<SessionChangeObserver> observer = new SessionChangeObserver(aListener);
|
||||
WinEventHub::Get()->AddObserver(observer);
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
} // namespace mozilla::widget
|
||||
|
|
|
@ -7,27 +7,109 @@
|
|||
#ifndef widget_windows_WinEventObserver_h
|
||||
#define widget_windows_WinEventObserver_h
|
||||
|
||||
#include <windef.h>
|
||||
#include <windows.h>
|
||||
|
||||
namespace mozilla::widget {
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsTHashSet.h"
|
||||
|
||||
class WinEventWindow {
|
||||
namespace mozilla {
|
||||
|
||||
namespace widget {
|
||||
|
||||
class WinEventObserver {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WinEventObserver)
|
||||
public:
|
||||
// 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();
|
||||
virtual void OnWinEventProc(HWND aHwnd, UINT aMsg, WPARAM aWParam,
|
||||
LPARAM aLParam) {}
|
||||
virtual void Destroy();
|
||||
|
||||
// (Do not call in real code.)
|
||||
static HWND GetHwndForTestingOnly();
|
||||
protected:
|
||||
virtual ~WinEventObserver();
|
||||
|
||||
private:
|
||||
// the hidden window's WNDPROC
|
||||
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
|
||||
bool mDestroyed = false;
|
||||
};
|
||||
|
||||
} // namespace mozilla::widget
|
||||
// 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; }
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
#endif // widget_windows_WinEventObserver_h
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "nsBaseWidget.h"
|
||||
#include "nsWindow.h"
|
||||
#include "transport/runnable_utils.h"
|
||||
#include "WinEventObserver.h"
|
||||
#include "WinUtils.h"
|
||||
|
||||
namespace mozilla::widget {
|
||||
|
@ -342,6 +341,12 @@ 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
|
||||
|
@ -423,6 +428,14 @@ void WinWindowOcclusionTracker::ShutDown() {
|
|||
}
|
||||
|
||||
void WinWindowOcclusionTracker::Destroy() {
|
||||
if (mDisplayStatusObserver) {
|
||||
mDisplayStatusObserver->Destroy();
|
||||
mDisplayStatusObserver = nullptr;
|
||||
}
|
||||
if (mSessionChangeObserver) {
|
||||
mSessionChangeObserver->Destroy();
|
||||
mSessionChangeObserver = nullptr;
|
||||
}
|
||||
if (mSerializedTaskDispatcher) {
|
||||
mSerializedTaskDispatcher->Destroy();
|
||||
}
|
||||
|
@ -439,6 +452,26 @@ 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",
|
||||
|
@ -500,7 +533,8 @@ WinWindowOcclusionTracker::WinWindowOcclusionTracker(
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG(LogLevel::Info, "WinWindowOcclusionTracker::WinWindowOcclusionTracker()");
|
||||
|
||||
WinEventWindow::Ensure();
|
||||
EnsureDisplayStatusObserver();
|
||||
EnsureSessionChangeObserver();
|
||||
|
||||
mSerializedTaskDispatcher = new SerializedTaskDispatcher();
|
||||
}
|
||||
|
@ -703,10 +737,11 @@ void WinWindowOcclusionTracker::UpdateOcclusionState(
|
|||
}
|
||||
}
|
||||
|
||||
void WinWindowOcclusionTracker::OnSessionChange(WPARAM aStatusCode) {
|
||||
void WinWindowOcclusionTracker::OnSessionChange(WPARAM aStatusCode,
|
||||
Maybe<bool> aIsCurrentSession) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!StaticPrefs::
|
||||
widget_windows_window_occlusion_tracking_session_lock_enabled()) {
|
||||
|
||||
if (aIsCurrentSession.isNothing() || !*aIsCurrentSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -728,11 +763,6 @@ 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,8 +7,7 @@
|
|||
#ifndef widget_windows_WinWindowOcclusionTracker_h
|
||||
#define widget_windows_WinWindowOcclusionTracker_h
|
||||
|
||||
#include <windef.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
@ -16,7 +15,10 @@
|
|||
#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"
|
||||
|
||||
|
@ -39,7 +41,8 @@ class UpdateOcclusionStateRunnable;
|
|||
|
||||
// This class handles window occlusion tracking by using HWND.
|
||||
// Implementation is borrowed from chromium's NativeWindowOcclusionTrackerWin.
|
||||
class WinWindowOcclusionTracker final {
|
||||
class WinWindowOcclusionTracker final : public DisplayStatusListener,
|
||||
public SessionChangeListener {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WinWindowOcclusionTracker)
|
||||
|
||||
|
@ -58,6 +61,12 @@ class WinWindowOcclusionTracker final {
|
|||
/// 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);
|
||||
|
@ -271,16 +280,15 @@ class WinWindowOcclusionTracker final {
|
|||
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);
|
||||
void OnSessionChange(WPARAM aStatusCode,
|
||||
Maybe<bool> aIsCurrentSession) override;
|
||||
|
||||
// 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);
|
||||
void OnDisplayStateChanged(bool aDisplayOn) override;
|
||||
|
||||
private:
|
||||
// Marks all root windows as either occluded, or if hwnd IsIconic, hidden.
|
||||
void MarkNonIconicWindowsOccluded();
|
||||
|
||||
|
@ -308,6 +316,10 @@ class WinWindowOcclusionTracker final {
|
|||
// 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;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче