diff options
Diffstat (limited to 'widget/windows/WinCompositorWindowThread.cpp')
-rw-r--r-- | widget/windows/WinCompositorWindowThread.cpp | 257 |
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 |