diff --git a/widget/tests/gtest/TestWinWindowOcclusionTrackerInteractive.cpp b/widget/tests/gtest/TestWinWindowOcclusionTrackerInteractive.cpp index 3217eda851df..9ee53d55c64c 100644 --- a/widget/tests/gtest/TestWinWindowOcclusionTrackerInteractive.cpp +++ b/widget/tests/gtest/TestWinWindowOcclusionTrackerInteractive.cpp @@ -108,6 +108,8 @@ class WinWindowOcclusionTrackerInteractiveTest : public ::testing::Test { RefPtr 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 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 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()); diff --git a/widget/windows/WinWindowOcclusionTracker.cpp b/widget/windows/WinWindowOcclusionTracker.cpp index f916f072811e..d4ff49c6aac9 100644 --- a/widget/windows/WinWindowOcclusionTracker.cpp +++ b/widget/windows/WinWindowOcclusionTracker.cpp @@ -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 mData; }; +class DelayedTaskRunnable : public Runnable { + public: + DelayedTaskRunnable(SerializedTaskDispatcher* aSerializedTaskDispatcher, + already_AddRefed aTask) + : Runnable("DelayedTaskRunnable"), + mSerializedTaskDispatcher(aSerializedTaskDispatcher), + mTask(aTask) {} + + NS_IMETHOD Run() override { + mSerializedTaskDispatcher->HandleDelayedTask(mTask.forget()); + return NS_OK; + } + + private: + RefPtr mSerializedTaskDispatcher; + RefPtr 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 = WrapRunnable( - RefPtr(this), - &SerializedTaskDispatcher::HandleDelayedTask, std::move(aTask)); + RefPtr 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 = + WrapRunnable(RefPtr( + 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 = + WrapRunnable(RefPtr( + 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 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 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 diff --git a/widget/windows/WinWindowOcclusionTracker.h b/widget/windows/WinWindowOcclusionTracker.h index 617a02310074..e6ab7fa3718d 100644 --- a/widget/windows/WinWindowOcclusionTracker.h +++ b/widget/windows/WinWindowOcclusionTracker.h @@ -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 mVirtualDesktopManager; // Used to serialize tasks related to mRootWindowHwndsOcclusionState.