Bug 1732737 - Add VirtualDesktop handling to window occlusion on Windows r=gfx-reviewers,bradwerth

Implementation of WindowOcclusionCalculator::Initialize() is borrowed from InitializeVirtualDesktopManagerTask::Run().
And implementation of WindowOcclusionCalculator::IsWindowOnCurrentVirtualDesktop() is borrowed from chromium's WindowOcclusionCalculator::IsWindowOnCurrentVirtualDesktop()

Differential Revision: https://phabricator.services.mozilla.com/D133327
This commit is contained in:
sotaro 2022-02-15 14:33:06 +00:00
Родитель 85788740ea
Коммит 2daf591edf
3 изменённых файлов: 141 добавлений и 8 удалений

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

@ -108,6 +108,8 @@ class WinWindowOcclusionTrackerInteractiveTest : public ::testing::Test {
RefPtr<MockWinWidget> window = MockWinWidget::Create(
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, /* aExStyle */ 0, aBounds);
EXPECT_NE(nullptr, window.get());
HWND hwnd = window->GetWnd();
::ShowWindow(hwnd, SW_SHOWNORMAL);
WinWindowOcclusionTracker::Get()->Enable(window, window->GetWnd());
return window;
}
@ -132,6 +134,7 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, SimpleOcclusion) {
window->SetExpectation(widget::OcclusionState::OCCLUDED);
CreateNativeWindowWithBounds(LayoutDeviceIntRect(0, 0, 100, 100));
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
EXPECT_FALSE(window->IsExpectingCall());
@ -140,10 +143,11 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, SimpleOcclusion) {
// Simple test partially covering a tracked window with a native window.
TEST_F(WinWindowOcclusionTrackerInteractiveTest, PartialOcclusion) {
RefPtr<MockWinWidget> window =
CreateTrackedWindowWithBounds(LayoutDeviceIntRect(0, 0, 100, 100));
CreateTrackedWindowWithBounds(LayoutDeviceIntRect(0, 0, 200, 200));
window->SetExpectation(widget::OcclusionState::VISIBLE);
CreateNativeWindowWithBounds(LayoutDeviceIntRect(0, 0, 50, 50));
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
EXPECT_FALSE(window->IsExpectingCall());
@ -163,6 +167,7 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, OffscreenOcclusion) {
CreateNativeWindowWithBounds(LayoutDeviceIntRect(screenLeft, 0, 50, 100));
window->SetExpectation(widget::OcclusionState::OCCLUDED);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
EXPECT_FALSE(window->IsExpectingCall());
@ -175,6 +180,8 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, SimpleVisible) {
window->SetExpectation(widget::OcclusionState::VISIBLE);
CreateNativeWindowWithBounds(LayoutDeviceIntRect(200, 0, 100, 100));
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
EXPECT_FALSE(window->IsExpectingCall());
@ -189,6 +196,7 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, SimpleHidden) {
::CloseWindow(window->GetWnd());
window->SetExpectation(widget::OcclusionState::HIDDEN);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
EXPECT_FALSE(window->IsExpectingCall());
@ -203,6 +211,7 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest,
CreateTrackedWindowWithBounds(LayoutDeviceIntRect(0, 0, 100, 100));
window->SetExpectation(widget::OcclusionState::VISIBLE);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
@ -214,6 +223,7 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest,
::CloseWindow(window->GetWnd());
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
@ -235,6 +245,7 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest,
::OpenIcon(window->GetWnd());
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
@ -242,6 +253,7 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest,
window->SetExpectation(widget::OcclusionState::OCCLUDED);
CreateNativeWindowWithBounds(LayoutDeviceIntRect(0, 0, 100, 100));
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
EXPECT_FALSE(window->IsExpectingCall());
@ -253,6 +265,7 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenVisibleOcclusion) {
CreateTrackedWindowWithBounds(LayoutDeviceIntRect(0, 0, 100, 100));
window->SetExpectation(widget::OcclusionState::VISIBLE);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
@ -266,6 +279,8 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenVisibleOcclusion) {
WTS_SESSION_LOCK, currentSessionId);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
MSG msg;
bool gotMessage =
::PeekMessageW(&msg, WinEventHub::Get()->GetWnd(), 0, 0, PM_REMOVE);
@ -287,6 +302,7 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenHiddenOcclusion) {
::CloseWindow(window->GetWnd());
window->SetExpectation(widget::OcclusionState::HIDDEN);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
@ -304,6 +320,8 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenHiddenOcclusion) {
WTS_SESSION_LOCK, currentSessionId);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
MSG msg;
bool gotMessage =
::PeekMessageW(&msg, WinEventHub::Get()->GetWnd(), 0, 0, PM_REMOVE);
@ -321,9 +339,11 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenHiddenOcclusion) {
// as occluded.
TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenDifferentSession) {
RefPtr<MockWinWidget> window =
CreateTrackedWindowWithBounds(LayoutDeviceIntRect(0, 0, 100, 100));
CreateTrackedWindowWithBounds(LayoutDeviceIntRect(0, 0, 200, 200));
window->SetExpectation(widget::OcclusionState::VISIBLE);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
@ -341,6 +361,8 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, LockScreenDifferentSession) {
// Create a native window to trigger occlusion calculation.
CreateNativeWindowWithBounds(LayoutDeviceIntRect(0, 0, 50, 50));
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
MSG msg;
bool gotMessage =
::PeekMessageW(&msg, WinEventHub::Get()->GetWnd(), 0, 0, PM_REMOVE);
@ -361,6 +383,8 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, DisplayOnOffHandling) {
CreateTrackedWindowWithBounds(LayoutDeviceIntRect(0, 0, 100, 100));
window->SetExpectation(widget::OcclusionState::VISIBLE);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
@ -369,12 +393,16 @@ TEST_F(WinWindowOcclusionTrackerInteractiveTest, DisplayOnOffHandling) {
// Turning display off and on isn't feasible, so send a notification.
OnDisplayStateChanged(/* aDisplayOn */ false);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
window->SetExpectation(widget::OcclusionState::VISIBLE);
OnDisplayStateChanged(/* aDisplayOn */ true);
while (window->IsExpectingCall()) {
WinWindowOcclusionTracker::Get()->TriggerCalculation();
NS_ProcessNextEvent(nullptr, /* aMayWait = */ true);
}
EXPECT_FALSE(window->IsExpectingCall());

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

@ -24,6 +24,7 @@
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/StaticPtr.h"
#include "nsBaseWidget.h"
#include "nsWindow.h"
#include "transport/runnable_utils.h"
#include "WinUtils.h"
@ -99,6 +100,8 @@ class SerializedTaskDispatcher {
bool IsOnCurrentThread();
private:
friend class DelayedTaskRunnable;
~SerializedTaskDispatcher();
struct Data {
@ -119,6 +122,24 @@ class SerializedTaskDispatcher {
DataMutex<Data> mData;
};
class DelayedTaskRunnable : public Runnable {
public:
DelayedTaskRunnable(SerializedTaskDispatcher* aSerializedTaskDispatcher,
already_AddRefed<Runnable> aTask)
: Runnable("DelayedTaskRunnable"),
mSerializedTaskDispatcher(aSerializedTaskDispatcher),
mTask(aTask) {}
NS_IMETHOD Run() override {
mSerializedTaskDispatcher->HandleDelayedTask(mTask.forget());
return NS_OK;
}
private:
RefPtr<SerializedTaskDispatcher> mSerializedTaskDispatcher;
RefPtr<Runnable> mTask;
};
SerializedTaskDispatcher::SerializedTaskDispatcher()
: mData("SerializedTaskDispatcher::mData") {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
@ -183,9 +204,8 @@ void SerializedTaskDispatcher::PostDelayedTaskToCalculator(
CALC_LOG(LogLevel::Debug,
"SerializedTaskDispatcher::PostDelayedTaskToCalculator()");
RefPtr<Runnable> runnable = WrapRunnable(
RefPtr<SerializedTaskDispatcher>(this),
&SerializedTaskDispatcher::HandleDelayedTask, std::move(aTask));
RefPtr<DelayedTaskRunnable> runnable =
new DelayedTaskRunnable(this, std::move(aTask));
MessageLoop* targetLoop =
WinWindowOcclusionTracker::OcclusionCalculatorLoop();
targetLoop->PostDelayedTask(runnable.forget(), aDelayMs);
@ -265,6 +285,9 @@ void SerializedTaskDispatcher::HandleTasks() {
// Get next task
{
auto data = mData.Lock();
if (data->mDestroyed) {
return;
}
frontTask = nullptr;
data->mTasks.pop();
@ -322,6 +345,12 @@ void WinWindowOcclusionTracker::Ensure() {
sTracker = new WinWindowOcclusionTracker(thread);
WindowOcclusionCalculator::CreateInstance();
RefPtr<Runnable> runnable =
WrapRunnable(RefPtr<WindowOcclusionCalculator>(
WindowOcclusionCalculator::GetInstance()),
&WindowOcclusionCalculator::Initialize);
sTracker->mSerializedTaskDispatcher->PostTaskToCalculator(runnable.forget());
}
/* static */
@ -343,6 +372,8 @@ void WinWindowOcclusionTracker::ShutDown() {
OcclusionCalculatorLoop()->PostTask(runnable.forget());
task.Wait();
sTracker->mThread->Stop();
WindowOcclusionCalculator::ClearInstance();
sTracker = nullptr;
}
@ -706,6 +737,14 @@ void WinWindowOcclusionTracker::MarkNonIconicWindowsOccluded() {
}
}
void WinWindowOcclusionTracker::TriggerCalculation() {
RefPtr<Runnable> runnable =
WrapRunnable(RefPtr<WindowOcclusionCalculator>(
WindowOcclusionCalculator::GetInstance()),
&WindowOcclusionCalculator::HandleTriggerCalculation);
mSerializedTaskDispatcher->PostTaskToCalculator(runnable.forget());
}
// static
BOOL WinWindowOcclusionTracker::DumpOccludingWindowsCallback(HWND aHWnd,
LPARAM aLParam) {
@ -765,6 +804,27 @@ void WinWindowOcclusionTracker::WindowOcclusionCalculator::ClearInstance() {
sCalculator = nullptr;
}
void WinWindowOcclusionTracker::WindowOcclusionCalculator::Initialize() {
MOZ_ASSERT(IsInWinWindowOcclusionThread());
MOZ_ASSERT(!mVirtualDesktopManager);
CALC_LOG(LogLevel::Info, "Initialize()");
#ifndef __MINGW32__
if (!IsWin10OrLater()) {
return;
}
RefPtr<IVirtualDesktopManager> desktopManager;
HRESULT hr = ::CoCreateInstance(
CLSID_VirtualDesktopManager, NULL, CLSCTX_INPROC_SERVER,
__uuidof(IVirtualDesktopManager), getter_AddRefs(desktopManager));
if (FAILED(hr)) {
return;
}
mVirtualDesktopManager = desktopManager;
#endif
}
void WinWindowOcclusionTracker::WindowOcclusionCalculator::Shutdown(
layers::SynchronousTask* aTask) {
MOZ_ASSERT(IsInWinWindowOcclusionThread());
@ -777,6 +837,7 @@ void WinWindowOcclusionTracker::WindowOcclusionCalculator::Shutdown(
mOcclusionUpdateRunnable->Cancel();
mOcclusionUpdateRunnable = nullptr;
}
mVirtualDesktopManager = nullptr;
}
void WinWindowOcclusionTracker::WindowOcclusionCalculator::
@ -837,6 +898,15 @@ void WinWindowOcclusionTracker::WindowOcclusionCalculator::
}
}
void WinWindowOcclusionTracker::WindowOcclusionCalculator::
HandleTriggerCalculation() {
MOZ_ASSERT(IsInWinWindowOcclusionThread());
CALC_LOG(LogLevel::Info, "HandleTriggerCalculation()");
MaybeRegisterEventHooks();
ScheduleOcclusionCalculationIfNeeded();
}
void WinWindowOcclusionTracker::WindowOcclusionCalculator::
MaybeRegisterEventHooks() {
if (mGlobalEventHooks.empty()) {
@ -1288,9 +1358,40 @@ Maybe<bool> WinWindowOcclusionTracker::WindowOcclusionCalculator::
return Some(true);
}
// XXX VirtualDesktopManager is going to be handled by Bug 1732737.
BOOL onCurrentDesktop;
HRESULT hr = mVirtualDesktopManager->IsWindowOnCurrentVirtualDesktop(
aHwnd, &onCurrentDesktop);
if (FAILED(hr)) {
// In this case, we do not know the window is in which virtual desktop.
return Nothing();
}
return Nothing();
if (onCurrentDesktop) {
return Some(true);
}
GUID workspaceGuid;
hr = mVirtualDesktopManager->GetWindowDesktopId(aHwnd, &workspaceGuid);
if (FAILED(hr)) {
// In this case, we do not know the window is in which virtual desktop.
return Nothing();
}
// IsWindowOnCurrentVirtualDesktop() is flaky for newly opened windows,
// which causes test flakiness. Occasionally, it incorrectly says a window
// is not on the current virtual desktop when it is. In this situation,
// it also returns GUID_NULL for the desktop id.
if (workspaceGuid == GUID_NULL) {
// In this case, we do not know if the window is in which virtual desktop.
// But we hanle it as on current virtual desktop.
// It does not cause a problem to window occlusion.
// Since if window is not on current virtual desktop, window size becomes
// (0, 0, 0, 0). It makes window occlusion handling explicit. It is
// necessary for gtest.
return Some(true);
}
return Some(false);
}
#undef LOG

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

@ -75,6 +75,8 @@ class WinWindowOcclusionTracker final : public DisplayStatusListener,
return mSerializedTaskDispatcher;
}
void TriggerCalculation();
void DumpOccludingWindows(HWND aHWnd);
private:
@ -99,6 +101,7 @@ class WinWindowOcclusionTracker final : public DisplayStatusListener,
// Returns existing WindowOcclusionCalculator instance.
static WindowOcclusionCalculator* GetInstance() { return sCalculator; }
void Initialize();
void Shutdown(layers::SynchronousTask* aTask);
void EnableOcclusionTrackingForWindow(HWND hwnd);
@ -107,6 +110,8 @@ class WinWindowOcclusionTracker final : public DisplayStatusListener,
// If a window becomes visible, makes sure event hooks are registered.
void HandleVisibilityChanged(bool aVisible);
void HandleTriggerCalculation();
private:
WindowOcclusionCalculator();
~WindowOcclusionCalculator();
@ -236,7 +241,6 @@ class WinWindowOcclusionTracker final : public DisplayStatusListener,
HWND mMovingWindow = 0;
// Only used on Win10+.
// XXX VirtualDesktopManager is going to be handled by Bug 1732737.
RefPtr<IVirtualDesktopManager> mVirtualDesktopManager;
// Used to serialize tasks related to mRootWindowHwndsOcclusionState.