Bug 1838234 - Implement Quarantine controls in extensions panel and context menus...
[gecko.git] / widget / windows / WinCompositorWindowThread.cpp
blobf2d28fd66f0772595a0a7f58103ff5e3d8949db6
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 #if WINVER < 0x0602
16 # define WS_EX_NOREDIRECTIONBITMAP 0x00200000L
17 #endif
19 namespace mozilla {
20 namespace widget {
22 static StaticRefPtr<WinCompositorWindowThread> sWinCompositorWindowThread;
24 /// A window procedure that logs when an input event is received to the gfx
25 /// error log
26 ///
27 /// This is done because this window is supposed to be WM_DISABLED, but
28 /// malfunctioning software may still end up targetting this window. If that
29 /// happens, it's almost-certainly a bug and should be brought to the attention
30 /// of the developers that are debugging the issue.
31 static LRESULT CALLBACK InputEventRejectingWindowProc(HWND window, UINT msg,
32 WPARAM wparam,
33 LPARAM lparam) {
34 switch (msg) {
35 case WM_LBUTTONDOWN:
36 case WM_LBUTTONUP:
37 case WM_RBUTTONDOWN:
38 case WM_RBUTTONUP:
39 case WM_MBUTTONDOWN:
40 case WM_MBUTTONUP:
41 case WM_MOUSEWHEEL:
42 case WM_MOUSEHWHEEL:
43 case WM_MOUSEMOVE:
44 case WM_KEYDOWN:
45 case WM_KEYUP:
46 case WM_SYSKEYDOWN:
47 case WM_SYSKEYUP:
48 gfxCriticalNoteOnce
49 << "The compositor window received an input event even though it's "
50 "disabled. There is likely malfunctioning "
51 "software on the user's machine.";
53 break;
54 default:
55 break;
57 return ::DefWindowProcW(window, msg, wparam, lparam);
60 WinCompositorWindowThread::WinCompositorWindowThread(base::Thread* aThread)
61 : mThread(aThread), mMonitor("WinCompositorWindowThread") {}
63 /* static */
64 WinCompositorWindowThread* WinCompositorWindowThread::Get() {
65 if (!sWinCompositorWindowThread ||
66 sWinCompositorWindowThread->mHasAttemptedShutdown) {
67 return nullptr;
69 return sWinCompositorWindowThread;
72 /* static */
73 void WinCompositorWindowThread::Start() {
74 MOZ_ASSERT(NS_IsMainThread());
76 base::Thread::Options options;
77 // HWND requests ui thread.
78 options.message_loop_type = MessageLoop::TYPE_UI;
80 if (sWinCompositorWindowThread) {
81 // Try to reuse the thread, which involves stopping and restarting it.
82 sWinCompositorWindowThread->mThread->Stop();
83 if (sWinCompositorWindowThread->mThread->StartWithOptions(options)) {
84 // Success!
85 sWinCompositorWindowThread->mHasAttemptedShutdown = false;
86 return;
88 // Restart failed, so null out our sWinCompositorWindowThread and
89 // try again with a new thread. This will cause the old singleton
90 // instance to be deallocated, which will destroy its mThread as well.
91 sWinCompositorWindowThread = nullptr;
94 base::Thread* thread = new base::Thread("WinCompositor");
95 if (!thread->StartWithOptions(options)) {
96 delete thread;
97 return;
100 sWinCompositorWindowThread = new WinCompositorWindowThread(thread);
103 /* static */
104 void WinCompositorWindowThread::ShutDown() {
105 MOZ_ASSERT(NS_IsMainThread());
106 MOZ_ASSERT(sWinCompositorWindowThread);
108 sWinCompositorWindowThread->mHasAttemptedShutdown = true;
110 // Our thread could hang while we're waiting for it to stop.
111 // Since we're shutting down, that's not a critical problem.
112 // We set a reasonable amount of time to wait for shutdown,
113 // and if it succeeds within that time, we correctly stop
114 // our thread by nulling out the refptr, which will cause it
115 // to be deallocated and join the thread. If it times out,
116 // we do nothing, which means that the thread will not be
117 // joined and sWinCompositorWindowThread memory will leak.
118 CVStatus status;
120 // It's important to hold the lock before posting the
121 // runnable. This ensures that the runnable can't begin
122 // until we've started our Wait, which prevents us from
123 // Waiting on a monitor that has already been notified.
124 MonitorAutoLock lock(sWinCompositorWindowThread->mMonitor);
126 static const TimeDuration TIMEOUT = TimeDuration::FromSeconds(2.0);
127 RefPtr<Runnable> runnable =
128 NewRunnableMethod("WinCompositorWindowThread::ShutDownTask",
129 sWinCompositorWindowThread.get(),
130 &WinCompositorWindowThread::ShutDownTask);
131 Loop()->PostTask(runnable.forget());
133 // Monitor uses SleepConditionVariableSRW, which can have
134 // spurious wakeups which are reported as timeouts, so we
135 // check timestamps to ensure that we've waited as long we
136 // intended to. If we wake early, we don't bother calculating
137 // a precise amount for the next wait; we just wait the same
138 // amount of time. This means timeout might happen after as
139 // much as 2x the TIMEOUT time.
140 TimeStamp timeStart = TimeStamp::NowLoRes();
141 do {
142 status = sWinCompositorWindowThread->mMonitor.Wait(TIMEOUT);
143 } while ((status == CVStatus::Timeout) &&
144 ((TimeStamp::NowLoRes() - timeStart) < TIMEOUT));
147 if (status == CVStatus::NoTimeout) {
148 sWinCompositorWindowThread = nullptr;
152 void WinCompositorWindowThread::ShutDownTask() {
153 MonitorAutoLock lock(mMonitor);
155 MOZ_ASSERT(IsInCompositorWindowThread());
156 mMonitor.NotifyAll();
159 /* static */
160 MessageLoop* WinCompositorWindowThread::Loop() {
161 return sWinCompositorWindowThread
162 ? sWinCompositorWindowThread->mThread->message_loop()
163 : nullptr;
166 /* static */
167 bool WinCompositorWindowThread::IsInCompositorWindowThread() {
168 return sWinCompositorWindowThread &&
169 sWinCompositorWindowThread->mThread->thread_id() ==
170 PlatformThread::CurrentId();
173 const wchar_t kClassNameCompositorInitalParent[] =
174 L"MozillaCompositorInitialParentClass";
175 const wchar_t kClassNameCompositor[] = L"MozillaCompositorWindowClass";
177 ATOM g_compositor_inital_parent_window_class;
178 ATOM g_compositor_window_class;
180 // This runs on the window owner thread.
181 void InitializeInitialParentWindowClass() {
182 if (g_compositor_inital_parent_window_class) {
183 return;
186 WNDCLASSW wc;
187 wc.style = 0;
188 wc.lpfnWndProc = ::DefWindowProcW;
189 wc.cbClsExtra = 0;
190 wc.cbWndExtra = 0;
191 wc.hInstance = GetModuleHandle(nullptr);
192 wc.hIcon = nullptr;
193 wc.hCursor = nullptr;
194 wc.hbrBackground = nullptr;
195 wc.lpszMenuName = nullptr;
196 wc.lpszClassName = kClassNameCompositorInitalParent;
197 g_compositor_inital_parent_window_class = ::RegisterClassW(&wc);
200 // This runs on the window owner thread.
201 void InitializeWindowClass() {
202 if (g_compositor_window_class) {
203 return;
206 WNDCLASSW wc;
207 wc.style = CS_OWNDC;
208 wc.lpfnWndProc = InputEventRejectingWindowProc;
209 wc.cbClsExtra = 0;
210 wc.cbWndExtra = 0;
211 wc.hInstance = GetModuleHandle(nullptr);
212 wc.hIcon = nullptr;
213 wc.hCursor = nullptr;
214 wc.hbrBackground = nullptr;
215 wc.lpszMenuName = nullptr;
216 wc.lpszClassName = kClassNameCompositor;
217 g_compositor_window_class = ::RegisterClassW(&wc);
220 /* static */
221 WinCompositorWnds WinCompositorWindowThread::CreateCompositorWindow() {
222 MOZ_ASSERT(Loop());
224 if (!Loop()) {
225 return WinCompositorWnds(nullptr, nullptr);
228 layers::SynchronousTask task("Create compositor window");
230 HWND initialParentWnd = nullptr;
231 HWND compositorWnd = nullptr;
233 RefPtr<Runnable> runnable = NS_NewRunnableFunction(
234 "WinCompositorWindowThread::CreateCompositorWindow::Runnable", [&]() {
235 layers::AutoCompleteTask complete(&task);
237 InitializeInitialParentWindowClass();
238 InitializeWindowClass();
240 // Create initial parent window.
241 // We could not directly create a compositor window with a main window
242 // as parent window, so instead create it with a temporary placeholder
243 // parent. Its parent is set as main window in UI process.
244 initialParentWnd =
245 ::CreateWindowEx(WS_EX_TOOLWINDOW, kClassNameCompositorInitalParent,
246 nullptr, WS_POPUP | WS_DISABLED, 0, 0, 1, 1,
247 nullptr, 0, GetModuleHandle(nullptr), 0);
248 if (!initialParentWnd) {
249 gfxCriticalNoteOnce << "Inital parent window failed "
250 << ::GetLastError();
251 return;
254 DWORD extendedStyle = WS_EX_NOPARENTNOTIFY | WS_EX_NOREDIRECTIONBITMAP;
256 if (!StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
257 extendedStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
260 compositorWnd = ::CreateWindowEx(
261 extendedStyle, kClassNameCompositor, nullptr,
262 WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 0, 0, 1, 1,
263 initialParentWnd, 0, GetModuleHandle(nullptr), 0);
264 if (!compositorWnd) {
265 gfxCriticalNoteOnce << "Compositor window failed "
266 << ::GetLastError();
270 Loop()->PostTask(runnable.forget());
272 task.Wait();
274 return WinCompositorWnds(compositorWnd, initialParentWnd);
277 /* static */
278 void WinCompositorWindowThread::DestroyCompositorWindow(
279 WinCompositorWnds aWnds) {
280 MOZ_ASSERT(aWnds.mCompositorWnd);
281 MOZ_ASSERT(aWnds.mInitialParentWnd);
282 MOZ_ASSERT(Loop());
284 if (!Loop()) {
285 return;
288 RefPtr<Runnable> runnable = NS_NewRunnableFunction(
289 "WinCompositorWidget::CreateNativeWindow::Runnable", [aWnds]() {
290 ::DestroyWindow(aWnds.mCompositorWnd);
291 ::DestroyWindow(aWnds.mInitialParentWnd);
294 Loop()->PostTask(runnable.forget());
297 } // namespace widget
298 } // namespace mozilla