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