1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/gl/child_window_win.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/compiler_specific.h"
11 #include "base/debug/alias.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/message_loop/message_pump_type.h"
14 #include "base/win/wrapped_window_proc.h"
15 #include "ui/gfx/win/hwnd_util.h"
16 #include "ui/gfx/win/window_impl.h"
17
18 namespace gl {
19
20 namespace {
21
22 ATOM g_window_class;
23
24 // This runs on the window owner thread.
InitializeWindowClass()25 void InitializeWindowClass() {
26 if (g_window_class)
27 return;
28
29 WNDCLASSEX intermediate_class;
30 base::win::InitializeWindowClass(
31 L"Intermediate D3D Window",
32 &base::win::WrappedWindowProc<::DefWindowProc>, CS_OWNDC, 0, 0, nullptr,
33 reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)), nullptr, nullptr,
34 nullptr, &intermediate_class);
35 g_window_class = RegisterClassEx(&intermediate_class);
36 if (!g_window_class) {
37 LOG(ERROR) << "RegisterClass failed.";
38 return;
39 }
40 }
41
42 // Hidden popup window used as a parent for the child surface window.
43 // Must be created and destroyed on the thread.
44 class HiddenPopupWindow : public gfx::WindowImpl {
45 public:
Create()46 static HWND Create() {
47 gfx::WindowImpl* window = new HiddenPopupWindow;
48
49 window->set_window_style(WS_POPUP);
50 window->set_window_ex_style(WS_EX_TOOLWINDOW);
51 window->Init(GetDesktopWindow(), gfx::Rect());
52 EnableWindow(window->hwnd(), FALSE);
53 // The |window| instance is now owned by the window user data.
54 DCHECK_EQ(window, gfx::GetWindowUserData(window->hwnd()));
55 return window->hwnd();
56 }
57
Destroy(HWND window)58 static void Destroy(HWND window) {
59 // This uses the fact that the window user data contains a pointer
60 // to gfx::WindowImpl instance.
61 gfx::WindowImpl* window_data =
62 reinterpret_cast<gfx::WindowImpl*>(gfx::GetWindowUserData(window));
63 DCHECK_EQ(window, window_data->hwnd());
64 DestroyWindow(window);
65 delete window_data;
66 }
67
68 private:
69 // Explicitly do nothing in Close. We do this as some external apps may get a
70 // handle to this window and attempt to close it.
OnClose()71 void OnClose() {}
72
73 CR_BEGIN_MSG_MAP_EX(HiddenPopupWindow)
74 CR_MSG_WM_CLOSE(OnClose)
75 CR_END_MSG_MAP()
76
77 CR_MSG_MAP_CLASS_DECLARATIONS(HiddenPopupWindow)
78 };
79
80 // This runs on the window owner thread.
CreateWindowsOnThread(const gfx::Size & size,base::WaitableEvent * event,HWND * child_window,HWND * parent_window)81 void CreateWindowsOnThread(const gfx::Size& size,
82 base::WaitableEvent* event,
83 HWND* child_window,
84 HWND* parent_window) {
85 InitializeWindowClass();
86 DCHECK(g_window_class);
87
88 // Create hidden parent window on the current thread.
89 *parent_window = HiddenPopupWindow::Create();
90 // Create child window.
91 // WS_EX_NOPARENTNOTIFY and WS_EX_LAYERED make the window transparent for
92 // input. WS_EX_NOREDIRECTIONBITMAP avoids allocating a
93 // bitmap that would otherwise be allocated with WS_EX_LAYERED, the bitmap is
94 // only necessary if using Gdi objects with the window.
95 HWND window = CreateWindowEx(
96 WS_EX_NOPARENTNOTIFY | WS_EX_LAYERED | WS_EX_TRANSPARENT |
97 WS_EX_NOREDIRECTIONBITMAP,
98 reinterpret_cast<wchar_t*>(g_window_class), L"",
99 WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 0, 0, size.width(),
100 size.height(), *parent_window, nullptr, nullptr, nullptr);
101 if (!window) {
102 logging::SystemErrorCode error = logging::GetLastSystemErrorCode();
103 base::debug::Alias(&error);
104 CHECK(false);
105 }
106 *child_window = window;
107 event->Signal();
108 }
109
110 // This runs on the main thread after the window was destroyed on window owner
111 // thread.
DestroyThread(std::unique_ptr<base::Thread> thread)112 void DestroyThread(std::unique_ptr<base::Thread> thread) {
113 thread->Stop();
114 }
115
116 // This runs on the window owner thread.
DestroyWindowsOnThread(HWND child_window,HWND hidden_popup_window)117 void DestroyWindowsOnThread(HWND child_window, HWND hidden_popup_window) {
118 DestroyWindow(child_window);
119 HiddenPopupWindow::Destroy(hidden_popup_window);
120 }
121
122 } // namespace
123
ChildWindowWin(HWND parent_window)124 ChildWindowWin::ChildWindowWin(HWND parent_window)
125 : parent_window_(parent_window) {}
126
Initialize()127 void ChildWindowWin::Initialize() {
128 if (window_)
129 return;
130
131 thread_ = std::make_unique<base::Thread>("Window owner thread");
132 base::Thread::Options options(base::MessagePumpType::UI, 0);
133 thread_->StartWithOptions(options);
134
135 base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
136 base::WaitableEvent::InitialState::NOT_SIGNALED);
137
138 RECT window_rect;
139 GetClientRect(parent_window_, &window_rect);
140
141 thread_->task_runner()->PostTask(
142 FROM_HERE,
143 base::BindOnce(&CreateWindowsOnThread, gfx::Rect(window_rect).size(),
144 &event, &window_, &initial_parent_window_));
145 event.Wait();
146 }
147
~ChildWindowWin()148 ChildWindowWin::~ChildWindowWin() {
149 if (thread_) {
150 scoped_refptr<base::TaskRunner> task_runner = thread_->task_runner();
151 task_runner->PostTaskAndReply(
152 FROM_HERE,
153 base::BindOnce(&DestroyWindowsOnThread, window_,
154 initial_parent_window_),
155 base::BindOnce(&DestroyThread, std::move(thread_)));
156 }
157 }
158
GetTaskRunnerForTesting()159 scoped_refptr<base::TaskRunner> ChildWindowWin::GetTaskRunnerForTesting() {
160 DCHECK(thread_);
161 return thread_->task_runner();
162 }
163
164 } // namespace gl
165