xref: /aosp_15_r20/external/cronet/base/win/message_window.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2013 The Chromium Authors
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 "base/win/message_window.h"
6 
7 #include <windows.h>
8 
9 #include <map>
10 #include <utility>
11 
12 #include "base/check.h"
13 #include "base/check_op.h"
14 #include "base/compiler_specific.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/memory/raw_ref.h"
19 #include "base/no_destructor.h"
20 #include "base/strings/string_util.h"
21 #include "base/thread_annotations.h"
22 #include "base/threading/thread_checker.h"
23 #include "base/threading/thread_local.h"
24 #include "base/win/current_module.h"
25 #include "base/win/resource_exhaustion.h"
26 #include "base/win/wrapped_window_proc.h"
27 
28 // To avoid conflicts with the macro from the Windows SDK...
29 #undef FindWindow
30 
31 const wchar_t kMessageWindowClassName[] = L"Chrome_MessageWindow";
32 
33 namespace {
34 
35 // This class can be accessed from multiple threads,
36 // this is handled by each thread having a different instance.
37 class MessageWindowMap {
38  public:
GetInstanceForCurrentThread()39   static MessageWindowMap& GetInstanceForCurrentThread() {
40     static base::NoDestructor<base::ThreadLocalOwnedPointer<MessageWindowMap>>
41         instance;
42     if (!instance->Get()) {
43       instance->Set(base::WrapUnique(new MessageWindowMap));
44     }
45     return *(instance->Get());
46   }
47 
48   MessageWindowMap(const MessageWindowMap&) = delete;
49   MessageWindowMap& operator=(const MessageWindowMap&) = delete;
50 
51   // Each key should only be inserted once.
Insert(HWND hwnd,base::win::MessageWindow & message_window)52   void Insert(HWND hwnd, base::win::MessageWindow& message_window) {
53     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
54     CHECK(map_.emplace(hwnd, message_window).second);
55   }
56 
57   // Erase should only be called on an existing key.
Erase(HWND hwnd)58   void Erase(HWND hwnd) {
59     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
60     // Check that exactly one element is erased from the map.
61     CHECK_EQ(map_.erase(hwnd), 1u);
62   }
63 
64   // Will return nullptr if `hwnd` is not in the map.
Get(HWND hwnd) const65   base::win::MessageWindow* Get(HWND hwnd) const {
66     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
67     if (auto search = map_.find(hwnd); search != map_.end()) {
68       return &(search->second.get());
69     }
70     return nullptr;
71   }
72 
73  private:
74   MessageWindowMap() = default;
75   THREAD_CHECKER(thread_checker_);
76   std::map<HWND, const raw_ref<base::win::MessageWindow>> map_
77       GUARDED_BY_CONTEXT(thread_checker_);
78 };
79 
80 }  // namespace
81 
82 namespace base {
83 namespace win {
84 
85 // Used along with LazyInstance to register a window class for message-only
86 // windows created by MessageWindow.
87 class MessageWindow::WindowClass {
88  public:
89   WindowClass();
90 
91   WindowClass(const WindowClass&) = delete;
92   WindowClass& operator=(const WindowClass&) = delete;
93 
94   ~WindowClass();
95 
atom()96   ATOM atom() { return atom_; }
instance()97   HINSTANCE instance() { return instance_; }
98 
99  private:
100   ATOM atom_ = 0;
101   HINSTANCE instance_ = CURRENT_MODULE();
102 };
103 
104 static LazyInstance<MessageWindow::WindowClass>::DestructorAtExit
105     g_window_class = LAZY_INSTANCE_INITIALIZER;
106 
WindowClass()107 MessageWindow::WindowClass::WindowClass() {
108   WNDCLASSEX window_class;
109   window_class.cbSize = sizeof(window_class);
110   window_class.style = 0;
111   window_class.lpfnWndProc = &WrappedWindowProc<WindowProc>;
112   window_class.cbClsExtra = 0;
113   window_class.cbWndExtra = 0;
114   window_class.hInstance = instance_;
115   window_class.hIcon = nullptr;
116   window_class.hCursor = nullptr;
117   window_class.hbrBackground = nullptr;
118   window_class.lpszMenuName = nullptr;
119   window_class.lpszClassName = kMessageWindowClassName;
120   window_class.hIconSm = nullptr;
121   atom_ = RegisterClassEx(&window_class);
122   if (atom_ == 0) {
123     PLOG(ERROR)
124         << "Failed to register the window class for a message-only window";
125     OnResourceExhausted();
126   }
127 }
128 
~WindowClass()129 MessageWindow::WindowClass::~WindowClass() {
130   if (atom_ != 0) {
131     BOOL result = UnregisterClass(MAKEINTATOM(atom_), instance_);
132     // Hitting this DCHECK usually means that some MessageWindow objects were
133     // leaked. For example not calling
134     // ui::Clipboard::DestroyClipboardForCurrentThread() results in a leaked
135     // MessageWindow.
136     DCHECK(result);
137   }
138 }
139 
140 MessageWindow::MessageWindow() = default;
141 
~MessageWindow()142 MessageWindow::~MessageWindow() {
143   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
144 
145   if (window_ != nullptr) {
146     BOOL result = DestroyWindow(window_);
147     DCHECK(result);
148   }
149 }
150 
Create(MessageCallback message_callback)151 bool MessageWindow::Create(MessageCallback message_callback) {
152   return DoCreate(std::move(message_callback), nullptr);
153 }
154 
CreateNamed(MessageCallback message_callback,const std::wstring & window_name)155 bool MessageWindow::CreateNamed(MessageCallback message_callback,
156                                 const std::wstring& window_name) {
157   return DoCreate(std::move(message_callback), window_name.c_str());
158 }
159 
160 // static
FindWindow(const std::wstring & window_name)161 HWND MessageWindow::FindWindow(const std::wstring& window_name) {
162   return FindWindowEx(HWND_MESSAGE, nullptr, kMessageWindowClassName,
163                       window_name.c_str());
164 }
165 
DoCreate(MessageCallback message_callback,const wchar_t * window_name)166 bool MessageWindow::DoCreate(MessageCallback message_callback,
167                              const wchar_t* window_name) {
168   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
169   DCHECK(message_callback_.is_null());
170   DCHECK(!window_);
171 
172   message_callback_ = std::move(message_callback);
173 
174   WindowClass& window_class = g_window_class.Get();
175   window_ =
176       CreateWindow(MAKEINTATOM(window_class.atom()), window_name, 0, 0, 0, 0, 0,
177                    HWND_MESSAGE, nullptr, window_class.instance(), this);
178   if (!window_) {
179     PLOG(ERROR) << "Failed to create a message-only window";
180     return false;
181   }
182 
183   return true;
184 }
185 
186 // static
WindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)187 LRESULT CALLBACK MessageWindow::WindowProc(HWND hwnd,
188                                            UINT message,
189                                            WPARAM wparam,
190                                            LPARAM lparam) {
191   // This can be called from different threads for different windows,
192   // each thread has its own MessageWindowMap instance.
193   auto& message_window_map = MessageWindowMap::GetInstanceForCurrentThread();
194   MessageWindow* self = message_window_map.Get(hwnd);
195 
196   // CreateWindow will send a WM_CREATE message during window creation.
197   if (UNLIKELY(!self && message == WM_CREATE)) {
198     CREATESTRUCT* const cs = reinterpret_cast<CREATESTRUCT*>(lparam);
199     self = reinterpret_cast<MessageWindow*>(cs->lpCreateParams);
200 
201     // Tell the MessageWindow instance the HWND that CreateWindow has produced.
202     self->window_ = hwnd;
203 
204     // Associate the MessageWindow instance with the HWND in the map.
205     message_window_map.Insert(hwnd, *self);
206   }
207 
208   if (UNLIKELY(!self)) {
209     return DefWindowProc(hwnd, message, wparam, lparam);
210   }
211 
212   LRESULT message_result = {};
213   if (!self->message_callback_.Run(message, wparam, lparam, &message_result)) {
214     message_result = DefWindowProc(hwnd, message, wparam, lparam);
215   }
216 
217   if (UNLIKELY(message == WM_DESTROY)) {
218     // Tell the MessageWindow instance that it no longer has an HWND.
219     self->window_ = nullptr;
220 
221     // Remove this HWND's MessageWindow from the map since it is going away.
222     message_window_map.Erase(hwnd);
223   }
224 
225   return message_result;
226 }
227 
228 }  // namespace win
229 }  // namespace base
230