Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / windows / WinCompositorWindowThread.cpp
blob3b06c098e651355ede9340824e0a645b0e4a752a
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "base/platform_thread.h"
8 #include "WinCompositorWindowThread.h"
9 #include "mozilla/gfx/Logging.h"
10 #include "mozilla/layers/SynchronousTask.h"
11 #include "mozilla/StaticPtr.h"
12 #include "transport/runnable_utils.h"
13 #include "mozilla/StaticPrefs_apz.h"
15 namespace mozilla {
16 namespace widget {
18 static StaticRefPtr<WinCompositorWindowThread> sWinCompositorWindowThread;
20 /// A window procedure that logs when an input event is received to the gfx
21 /// error log
22 ///
23 /// This is done because this window is supposed to be WM_DISABLED, but
24 /// malfunctioning software may still end up targetting this window. If that
25 /// happens, it's almost-certainly a bug and should be brought to the attention
26 /// of the developers that are debugging the issue.
27 static LRESULT CALLBACK InputEventRejectingWindowProc(HWND window, UINT msg,
28 WPARAM wparam,
29 LPARAM lparam) {
30 switch (msg) {
31 case WM_LBUTTONDOWN:
32 case WM_LBUTTONUP:
33 case WM_RBUTTONDOWN:
34 case WM_RBUTTONUP:
35 case WM_MBUTTONDOWN:
36 case WM_MBUTTONUP:
37 case WM_MOUSEWHEEL:
38 case WM_MOUSEHWHEEL:
39 case WM_MOUSEMOVE:
40 case WM_KEYDOWN:
41 case WM_KEYUP:
42 case WM_SYSKEYDOWN:
43 case WM_SYSKEYUP:
44 gfxCriticalNoteOnce
45 << "The compositor window received an input event even though it's "
46 "disabled. There is likely malfunctioning "
47 "software on the user's machine.";
49 break;
50 default:
51 break;
53 return ::DefWindowProcW(window, msg, wparam, lparam);
56 WinCompositorWindowThread::WinCompositorWindowThread(base::Thread* aThread)
57 : mThread(aThread), mMonitor("WinCompositorWindowThread") {}
59 /* static */
60 WinCompositorWindowThread* WinCompositorWindowThread::Get() {
61 if (!sWinCompositorWindowThread ||
62 sWinCompositorWindowThread->mHasAttemptedShutdown) {
63 return nullptr;
65 return sWinCompositorWindowThread;
68 /* static */
69 void WinCompositorWindowThread::Start() {
70 MOZ_ASSERT(NS_IsMainThread());
72 base::Thread::Options options;
73 // HWND requests ui thread.
74 options.message_loop_type = MessageLoop::TYPE_UI;
76 if (sWinCompositorWindowThread) {
77 // Try to reuse the thread, which involves stopping and restarting it.
78 sWinCompositorWindowThread->mThread->Stop();
79 if (sWinCompositorWindowThread->mThread->StartWithOptions(options)) {
80 // Success!
81 sWinCompositorWindowThread->mHasAttemptedShutdown = false;
82 return;
84 // Restart failed, so null out our sWinCompositorWindowThread and
85 // try again with a new thread. This will cause the old singleton
86 // instance to be deallocated, which will destroy its mThread as well.
87 sWinCompositorWindowThread = nullptr;
90 base::Thread* thread = new base::Thread("WinCompositor");
91 if (!thread->StartWithOptions(options)) {
92 delete thread;
93 return;
96 sWinCompositorWindowThread = new WinCompositorWindowThread(thread);
99 /* static */
100 void WinCompositorWindowThread::ShutDown() {
101 MOZ_ASSERT(NS_IsMainThread());
102 MOZ_ASSERT(sWinCompositorWindowThread);
104 sWinCompositorWindowThread->mHasAttemptedShutdown = true;
106 // Our thread could hang while we're waiting for it to stop.
107 // Since we're shutting down, that's not a critical problem.
108 // We set a reasonable amount of time to wait for shutdown,
109 // and if it succeeds within that time, we correctly stop
110 // our thread by nulling out the refptr, which will cause it
111 // to be deallocated and join the thread. If it times out,
112 // we do nothing, which means that the thread will not be
113 // joined and sWinCompositorWindowThread memory will leak.
114 CVStatus status;
116 // It's important to hold the lock before posting the
117 // runnable. This ensures that the runnable can't begin
118 // until we've started our Wait, which prevents us from
119 // Waiting on a monitor that has already been notified.
120 MonitorAutoLock lock(sWinCompositorWindowThread->mMonitor);
122 static const TimeDuration TIMEOUT = TimeDuration::FromSeconds(2.0);
123 RefPtr<Runnable> runnable =
124 NewRunnableMethod("WinCompositorWindowThread::ShutDownTask",
125 sWinCompositorWindowThread.get(),
126 &WinCompositorWindowThread::ShutDownTask);
127 Loop()->PostTask(runnable.forget());
129 // Monitor uses SleepConditionVariableSRW, which can have
130 // spurious wakeups which are reported as timeouts, so we
131 // check timestamps to ensure that we've waited as long we
132 // intended to. If we wake early, we don't bother calculating
133 // a precise amount for the next wait; we just wait the same
134 // amount of time. This means timeout might happen after as
135 // much as 2x the TIMEOUT time.
136 TimeStamp timeStart = TimeStamp::NowLoRes();
137 do {
138 status = sWinCompositorWindowThread->mMonitor.Wait(TIMEOUT);
139 } while ((status == CVStatus::Timeout) &&
140 ((TimeStamp::NowLoRes() - timeStart) < TIMEOUT));
143 if (status == CVStatus::NoTimeout) {
144 sWinCompositorWindowThread = nullptr;
148 void WinCompositorWindowThread::ShutDownTask() {
149 MonitorAutoLock lock(mMonitor);
151 MOZ_ASSERT(IsInCompositorWindowThread());
152 mMonitor.NotifyAll();
155 /* static */
156 MessageLoop* WinCompositorWindowThread::Loop() {
157 return sWinCompositorWindowThread
158 ? sWinCompositorWindowThread->mThread->message_loop()
159 : nullptr;
162 /* static */
163 bool WinCompositorWindowThread::IsInCompositorWindowThread() {
164 return sWinCompositorWindowThread &&
165 sWinCompositorWindowThread->mThread->thread_id() ==
166 PlatformThread::CurrentId();
169 const wchar_t kClassNameCompositorInitalParent[] =
170 L"MozillaCompositorInitialParentClass";
171 const wchar_t kClassNameCompositor[] = L"MozillaCompositorWindowClass";
173 ATOM g_compositor_inital_parent_window_class;
174 ATOM g_compositor_window_class;
176 // This runs on the window owner thread.
177 void InitializeInitialParentWindowClass() {
178 if (g_compositor_inital_parent_window_class) {
179 return;
182 WNDCLASSW wc;
183 wc.style = 0;
184 wc.lpfnWndProc = ::DefWindowProcW;
185 wc.cbClsExtra = 0;
186 wc.cbWndExtra = 0;
187 wc.hInstance = GetModuleHandle(nullptr);
188 wc.hIcon = nullptr;
189 wc.hCursor = nullptr;
190 wc.hbrBackground = nullptr;
191 wc.lpszMenuName = nullptr;
192 wc.lpszClassName = kClassNameCompositorInitalParent;
193 g_compositor_inital_parent_window_class = ::RegisterClassW(&wc);
196 // This runs on the window owner thread.
197 void InitializeWindowClass() {
198 if (g_compositor_window_class) {
199 return;
202 WNDCLASSW wc;
203 wc.style = CS_OWNDC;
204 wc.lpfnWndProc = InputEventRejectingWindowProc;
205 wc.cbClsExtra = 0;
206 wc.cbWndExtra = 0;
207 wc.hInstance = GetModuleHandle(nullptr);
208 wc.hIcon = nullptr;
209 wc.hCursor = nullptr;
210 wc.hbrBackground = nullptr;
211 wc.lpszMenuName = nullptr;
212 wc.lpszClassName = kClassNameCompositor;
213 g_compositor_window_class = ::RegisterClassW(&wc);
216 /* static */
217 WinCompositorWnds WinCompositorWindowThread::CreateCompositorWindow() {
218 MOZ_ASSERT(Loop());
220 if (!Loop()) {
221 return WinCompositorWnds(nullptr, nullptr);
224 layers::SynchronousTask task("Create compositor window");
226 HWND initialParentWnd = nullptr;
227 HWND compositorWnd = nullptr;
229 RefPtr<Runnable> runnable = NS_NewRunnableFunction(
230 "WinCompositorWindowThread::CreateCompositorWindow::Runnable", [&]() {
231 layers::AutoCompleteTask complete(&task);
233 InitializeInitialParentWindowClass();
234 InitializeWindowClass();
236 // Create initial parent window.
237 // We could not directly create a compositor window with a main window
238 // as parent window, so instead create it with a temporary placeholder
239 // parent. Its parent is set as main window in UI process.
240 initialParentWnd =
241 ::CreateWindowEx(WS_EX_TOOLWINDOW, kClassNameCompositorInitalParent,
242 nullptr, WS_POPUP | WS_DISABLED, 0, 0, 1, 1,
243 nullptr, 0, GetModuleHandle(nullptr), 0);
244 if (!initialParentWnd) {
245 gfxCriticalNoteOnce << "Inital parent window failed "
246 << ::GetLastError();
247 return;
250 DWORD extendedStyle = WS_EX_NOPARENTNOTIFY | WS_EX_NOREDIRECTIONBITMAP;
252 if (!StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
253 extendedStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
256 compositorWnd = ::CreateWindowEx(
257 extendedStyle, kClassNameCompositor, nullptr,
258 WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 0, 0, 1, 1,
259 initialParentWnd, 0, GetModuleHandle(nullptr), 0);
260 if (!compositorWnd) {
261 gfxCriticalNoteOnce << "Compositor window failed "
262 << ::GetLastError();
266 Loop()->PostTask(runnable.forget());
268 task.Wait();
270 return WinCompositorWnds(compositorWnd, initialParentWnd);
273 /* static */
274 void WinCompositorWindowThread::DestroyCompositorWindow(
275 WinCompositorWnds aWnds) {
276 MOZ_ASSERT(aWnds.mCompositorWnd);
277 MOZ_ASSERT(aWnds.mInitialParentWnd);
278 MOZ_ASSERT(Loop());
280 if (!Loop()) {
281 return;
284 RefPtr<Runnable> runnable = NS_NewRunnableFunction(
285 "WinCompositorWidget::CreateNativeWindow::Runnable", [aWnds]() {
286 ::DestroyWindow(aWnds.mCompositorWnd);
287 ::DestroyWindow(aWnds.mInitialParentWnd);
290 Loop()->PostTask(runnable.forget());
293 } // namespace widget
294 } // namespace mozilla