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