summaryrefslogtreecommitdiffstats
path: root/widget/windows/WinCompositorWindowThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--widget/windows/WinCompositorWindowThread.cpp257
1 files changed, 257 insertions, 0 deletions
diff --git a/widget/windows/WinCompositorWindowThread.cpp b/widget/windows/WinCompositorWindowThread.cpp
new file mode 100644
index 0000000000..9962ece837
--- /dev/null
+++ b/widget/windows/WinCompositorWindowThread.cpp
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "base/platform_thread.h"
+#include "WinCompositorWindowThread.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/SynchronousTask.h"
+#include "mozilla/StaticPtr.h"
+#include "transport/runnable_utils.h"
+#include "mozilla/StaticPrefs_apz.h"
+
+#if WINVER < 0x0602
+# define WS_EX_NOREDIRECTIONBITMAP 0x00200000L
+#endif
+
+namespace mozilla {
+namespace widget {
+
+static StaticRefPtr<WinCompositorWindowThread> sWinCompositorWindowThread;
+
+/// A window procedure that logs when an input event is received to the gfx
+/// error log
+///
+/// This is done because this window is supposed to be WM_DISABLED, but
+/// malfunctioning software may still end up targetting this window. If that
+/// happens, it's almost-certainly a bug and should be brought to the attention
+/// of the developers that are debugging the issue.
+static LRESULT CALLBACK InputEventRejectingWindowProc(HWND window, UINT msg,
+ WPARAM wparam,
+ LPARAM lparam) {
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ case WM_MOUSEWHEEL:
+ case WM_MOUSEHWHEEL:
+ case WM_MOUSEMOVE:
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ gfxCriticalNoteOnce
+ << "The compositor window received an input event even though it's "
+ "disabled. There is likely malfunctioning "
+ "software on the user's machine.";
+
+ break;
+ default:
+ break;
+ }
+ return ::DefWindowProcW(window, msg, wparam, lparam);
+}
+
+WinCompositorWindowThread::WinCompositorWindowThread(base::Thread* aThread)
+ : mThread(aThread) {}
+
+WinCompositorWindowThread::~WinCompositorWindowThread() { delete mThread; }
+
+/* static */
+WinCompositorWindowThread* WinCompositorWindowThread::Get() {
+ return sWinCompositorWindowThread;
+}
+
+/* static */
+void WinCompositorWindowThread::Start() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sWinCompositorWindowThread);
+
+ base::Thread* thread = new base::Thread("WinCompositor");
+
+ base::Thread::Options options;
+ // HWND requests ui thread.
+ options.message_loop_type = MessageLoop::TYPE_UI;
+
+ if (!thread->StartWithOptions(options)) {
+ delete thread;
+ return;
+ }
+
+ sWinCompositorWindowThread = new WinCompositorWindowThread(thread);
+}
+
+/* static */
+void WinCompositorWindowThread::ShutDown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sWinCompositorWindowThread);
+
+ // Our shutdown task could hang. Since we're shutting down,
+ // that's not a critical problem. We set a reasonable amount
+ // of time to wait for shutdown, and if it succeeds within
+ // that time, we correctly stop our thread. If it times out,
+ // we just leak the memory and proceed.
+ static const PRIntervalTime TIMEOUT = PR_TicksPerSecond() * 2;
+ layers::SynchronousTask task("WinCompositorWindowThread");
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<WinCompositorWindowThread>(sWinCompositorWindowThread.get()),
+ &WinCompositorWindowThread::ShutDownTask, &task);
+ sWinCompositorWindowThread->Loop()->PostTask(runnable.forget());
+ nsresult rv = task.Wait(TIMEOUT);
+ if (rv == NS_OK) {
+ sWinCompositorWindowThread->mThread->Stop();
+ }
+
+ sWinCompositorWindowThread = nullptr;
+}
+
+void WinCompositorWindowThread::ShutDownTask(layers::SynchronousTask* aTask) {
+ layers::AutoCompleteTask complete(aTask);
+ MOZ_ASSERT(IsInCompositorWindowThread());
+}
+
+/* static */
+MessageLoop* WinCompositorWindowThread::Loop() {
+ return sWinCompositorWindowThread
+ ? sWinCompositorWindowThread->mThread->message_loop()
+ : nullptr;
+}
+
+/* static */
+bool WinCompositorWindowThread::IsInCompositorWindowThread() {
+ return sWinCompositorWindowThread &&
+ sWinCompositorWindowThread->mThread->thread_id() ==
+ PlatformThread::CurrentId();
+}
+
+const wchar_t kClassNameCompositorInitalParent[] =
+ L"MozillaCompositorInitialParentClass";
+const wchar_t kClassNameCompositor[] = L"MozillaCompositorWindowClass";
+
+ATOM g_compositor_inital_parent_window_class;
+ATOM g_compositor_window_class;
+
+// This runs on the window owner thread.
+void InitializeInitialParentWindowClass() {
+ if (g_compositor_inital_parent_window_class) {
+ return;
+ }
+
+ WNDCLASSW wc;
+ wc.style = 0;
+ wc.lpfnWndProc = ::DefWindowProcW;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = GetModuleHandle(nullptr);
+ wc.hIcon = nullptr;
+ wc.hCursor = nullptr;
+ wc.hbrBackground = nullptr;
+ wc.lpszMenuName = nullptr;
+ wc.lpszClassName = kClassNameCompositorInitalParent;
+ g_compositor_inital_parent_window_class = ::RegisterClassW(&wc);
+}
+
+// This runs on the window owner thread.
+void InitializeWindowClass() {
+ if (g_compositor_window_class) {
+ return;
+ }
+
+ WNDCLASSW wc;
+ wc.style = CS_OWNDC;
+ wc.lpfnWndProc = InputEventRejectingWindowProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = GetModuleHandle(nullptr);
+ wc.hIcon = nullptr;
+ wc.hCursor = nullptr;
+ wc.hbrBackground = nullptr;
+ wc.lpszMenuName = nullptr;
+ wc.lpszClassName = kClassNameCompositor;
+ g_compositor_window_class = ::RegisterClassW(&wc);
+}
+
+/* static */
+WinCompositorWnds WinCompositorWindowThread::CreateCompositorWindow() {
+ MOZ_ASSERT(Loop());
+
+ if (!Loop()) {
+ return WinCompositorWnds(nullptr, nullptr);
+ }
+
+ layers::SynchronousTask task("Create compositor window");
+
+ HWND initialParentWnd = nullptr;
+ HWND compositorWnd = nullptr;
+
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+ "WinCompositorWindowThread::CreateCompositorWindow::Runnable", [&]() {
+ layers::AutoCompleteTask complete(&task);
+
+ InitializeInitialParentWindowClass();
+ InitializeWindowClass();
+
+ // Create initial parent window.
+ // We could not directly create a compositor window with a main window
+ // as parent window, so instead create it with a temporary placeholder
+ // parent. Its parent is set as main window in UI process.
+ initialParentWnd =
+ ::CreateWindowEx(WS_EX_TOOLWINDOW, kClassNameCompositorInitalParent,
+ nullptr, WS_POPUP | WS_DISABLED, 0, 0, 1, 1,
+ nullptr, 0, GetModuleHandle(nullptr), 0);
+ if (!initialParentWnd) {
+ gfxCriticalNoteOnce << "Inital parent window failed "
+ << ::GetLastError();
+ return;
+ }
+
+ DWORD extendedStyle = WS_EX_NOPARENTNOTIFY | WS_EX_NOREDIRECTIONBITMAP;
+
+ if (!StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
+ extendedStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
+ }
+
+ compositorWnd = ::CreateWindowEx(
+ extendedStyle, kClassNameCompositor, nullptr,
+ WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 0, 0, 1, 1,
+ initialParentWnd, 0, GetModuleHandle(nullptr), 0);
+ if (!compositorWnd) {
+ gfxCriticalNoteOnce << "Compositor window failed "
+ << ::GetLastError();
+ }
+ });
+
+ Loop()->PostTask(runnable.forget());
+
+ task.Wait();
+
+ return WinCompositorWnds(compositorWnd, initialParentWnd);
+}
+
+/* static */
+void WinCompositorWindowThread::DestroyCompositorWindow(
+ WinCompositorWnds aWnds) {
+ MOZ_ASSERT(aWnds.mCompositorWnd);
+ MOZ_ASSERT(aWnds.mInitialParentWnd);
+ MOZ_ASSERT(Loop());
+
+ if (!Loop()) {
+ return;
+ }
+
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+ "WinCompositorWidget::CreateNativeWindow::Runnable", [aWnds]() {
+ ::DestroyWindow(aWnds.mCompositorWnd);
+ ::DestroyWindow(aWnds.mInitialParentWnd);
+ });
+
+ Loop()->PostTask(runnable.forget());
+}
+
+} // namespace widget
+} // namespace mozilla