1 // Copyright (c) 2012 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/gfx/win/window_impl.h" 6 7 #include <list> 8 9 #include "base/bind.h" 10 #include "base/debug/alias.h" 11 #include "base/memory/singleton.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/synchronization/lock.h" 14 #include "base/win/wrapped_window_proc.h" 15 #include "ui/gfx/win/hwnd_util.h" 16 17 namespace gfx { 18 19 static const DWORD kWindowDefaultChildStyle = 20 WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; 21 static const DWORD kWindowDefaultStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN; 22 static const DWORD kWindowDefaultExStyle = 0; 23 24 /////////////////////////////////////////////////////////////////////////////// 25 // WindowImpl class tracking. 26 27 // Several external scripts rely explicitly on this base class name for 28 // acquiring the window handle and will break if this is modified! 29 // static 30 const wchar_t* const WindowImpl::kBaseClassName = L"Chrome_WidgetWin_"; 31 32 // WindowImpl class information used for registering unique windows. 33 struct ClassInfo { 34 UINT style; 35 HICON icon; 36 37 ClassInfo(int style, HICON icon) 38 : style(style), 39 icon(icon) {} 40 41 // Compares two ClassInfos. Returns true if all members match. 42 bool Equals(const ClassInfo& other) const { 43 return (other.style == style && other.icon == icon); 44 } 45 }; 46 47 // WARNING: this class may be used on multiple threads. 48 class ClassRegistrar { 49 public: 50 ~ClassRegistrar(); 51 52 static ClassRegistrar* GetInstance(); 53 54 void UnregisterClasses(); 55 56 // Returns the atom identifying the class matching |class_info|, 57 // creating and registering a new class if the class is not yet known. 58 ATOM RetrieveClassAtom(const ClassInfo& class_info); 59 60 private: 61 // Represents a registered window class. 62 struct RegisteredClass { 63 RegisteredClass(const ClassInfo& info, 64 const base::string16& name, 65 ATOM atom, 66 HINSTANCE instance); 67 68 // Info used to create the class. 69 ClassInfo info; 70 71 // The name given to the window class 72 base::string16 name; 73 74 // The atom identifying the window class. 75 ATOM atom; 76 77 // The handle of the module containing the window proceedure. 78 HMODULE instance; 79 }; 80 81 ClassRegistrar(); 82 friend struct DefaultSingletonTraits<ClassRegistrar>; 83 84 typedef std::list<RegisteredClass> RegisteredClasses; 85 RegisteredClasses registered_classes_; 86 87 // Counter of how many classes have been registered so far. 88 int registered_count_; 89 90 base::Lock lock_; 91 92 DISALLOW_COPY_AND_ASSIGN(ClassRegistrar); 93 }; 94 95 ClassRegistrar::~ClassRegistrar() {} 96 97 // static 98 ClassRegistrar* ClassRegistrar::GetInstance() { 99 return Singleton<ClassRegistrar, 100 LeakySingletonTraits<ClassRegistrar> >::get(); 101 } 102 103 void ClassRegistrar::UnregisterClasses() { 104 for (RegisteredClasses::iterator i = registered_classes_.begin(); 105 i != registered_classes_.end(); ++i) { 106 if (UnregisterClass(MAKEINTATOM(i->atom), i->instance)) { 107 registered_classes_.erase(i); 108 } else { 109 LOG(ERROR) << "Failed to unregister class " << i->name 110 << ". Error = " << GetLastError(); 111 } 112 } 113 } 114 115 ATOM ClassRegistrar::RetrieveClassAtom(const ClassInfo& class_info) { 116 base::AutoLock auto_lock(lock_); 117 for (RegisteredClasses::const_iterator i = registered_classes_.begin(); 118 i != registered_classes_.end(); ++i) { 119 if (class_info.Equals(i->info)) 120 return i->atom; 121 } 122 123 // No class found, need to register one. 124 base::string16 name = base::string16(WindowImpl::kBaseClassName) + 125 base::IntToString16(registered_count_++); 126 127 WNDCLASSEX window_class; 128 base::win::InitializeWindowClass( 129 name.c_str(), 130 &base::win::WrappedWindowProc<WindowImpl::WndProc>, 131 class_info.style, 132 0, 133 0, 134 NULL, 135 reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)), 136 NULL, 137 class_info.icon, 138 class_info.icon, 139 &window_class); 140 HMODULE instance = window_class.hInstance; 141 ATOM atom = RegisterClassEx(&window_class); 142 CHECK(atom) << GetLastError(); 143 144 registered_classes_.push_back(RegisteredClass( 145 class_info, name, atom, instance)); 146 147 return atom; 148 } 149 150 ClassRegistrar::RegisteredClass::RegisteredClass(const ClassInfo& info, 151 const base::string16& name, 152 ATOM atom, 153 HMODULE instance) 154 : info(info), 155 name(name), 156 atom(atom), 157 instance(instance) {} 158 159 ClassRegistrar::ClassRegistrar() : registered_count_(0) {} 160 161 162 /////////////////////////////////////////////////////////////////////////////// 163 // WindowImpl, public 164 165 WindowImpl::WindowImpl() 166 : window_style_(0), 167 window_ex_style_(kWindowDefaultExStyle), 168 class_style_(CS_DBLCLKS), 169 hwnd_(NULL), 170 got_create_(false), 171 got_valid_hwnd_(false), 172 destroyed_(NULL) { 173 } 174 175 WindowImpl::~WindowImpl() { 176 if (destroyed_) 177 *destroyed_ = true; 178 ClearUserData(); 179 } 180 181 // static 182 void WindowImpl::UnregisterClassesAtExit() { 183 base::AtExitManager::RegisterTask( 184 base::Bind(&ClassRegistrar::UnregisterClasses, 185 base::Unretained(ClassRegistrar::GetInstance()))); 186 } 187 188 void WindowImpl::Init(HWND parent, const Rect& bounds) { 189 if (window_style_ == 0) 190 window_style_ = parent ? kWindowDefaultChildStyle : kWindowDefaultStyle; 191 192 if (parent == HWND_DESKTOP) { 193 // Only non-child windows can have HWND_DESKTOP (0) as their parent. 194 CHECK((window_style_ & WS_CHILD) == 0); 195 parent = GetWindowToParentTo(false); 196 } else if (parent == ::GetDesktopWindow()) { 197 // Any type of window can have the "Desktop Window" as their parent. 198 parent = GetWindowToParentTo(true); 199 } else if (parent != HWND_MESSAGE) { 200 CHECK(::IsWindow(parent)); 201 } 202 203 int x, y, width, height; 204 if (bounds.IsEmpty()) { 205 x = y = width = height = CW_USEDEFAULT; 206 } else { 207 x = bounds.x(); 208 y = bounds.y(); 209 width = bounds.width(); 210 height = bounds.height(); 211 } 212 213 ATOM atom = GetWindowClassAtom(); 214 bool destroyed = false; 215 destroyed_ = &destroyed; 216 HWND hwnd = CreateWindowEx(window_ex_style_, 217 reinterpret_cast<wchar_t*>(atom), NULL, 218 window_style_, x, y, width, height, 219 parent, NULL, NULL, this); 220 221 // First nccalcszie (during CreateWindow) for captioned windows is 222 // deliberately ignored so force a second one here to get the right 223 // non-client set up. 224 if (hwnd && (window_style_ & WS_CAPTION)) { 225 SetWindowPos(hwnd, NULL, 0, 0, 0, 0, 226 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | 227 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); 228 } 229 230 if (!hwnd_ && GetLastError() == 0) { 231 base::debug::Alias(&destroyed); 232 base::debug::Alias(&hwnd); 233 bool got_create = got_create_; 234 base::debug::Alias(&got_create); 235 bool got_valid_hwnd = got_valid_hwnd_; 236 base::debug::Alias(&got_valid_hwnd); 237 WNDCLASSEX class_info; 238 memset(&class_info, 0, sizeof(WNDCLASSEX)); 239 class_info.cbSize = sizeof(WNDCLASSEX); 240 BOOL got_class = GetClassInfoEx(GetModuleHandle(NULL), 241 reinterpret_cast<wchar_t*>(atom), 242 &class_info); 243 base::debug::Alias(&got_class); 244 bool procs_match = got_class && class_info.lpfnWndProc == 245 base::win::WrappedWindowProc<&WindowImpl::WndProc>; 246 base::debug::Alias(&procs_match); 247 CHECK(false); 248 } 249 if (!destroyed) 250 destroyed_ = NULL; 251 252 CheckWindowCreated(hwnd_); 253 254 // The window procedure should have set the data for us. 255 CHECK_EQ(this, GetWindowUserData(hwnd)); 256 } 257 258 HICON WindowImpl::GetDefaultWindowIcon() const { 259 return NULL; 260 } 261 262 LRESULT WindowImpl::OnWndProc(UINT message, WPARAM w_param, LPARAM l_param) { 263 LRESULT result = 0; 264 265 HWND hwnd = hwnd_; 266 if (message == WM_NCDESTROY) 267 hwnd_ = NULL; 268 269 // Handle the message if it's in our message map; otherwise, let the system 270 // handle it. 271 if (!ProcessWindowMessage(hwnd, message, w_param, l_param, result)) 272 result = DefWindowProc(hwnd, message, w_param, l_param); 273 274 return result; 275 } 276 277 void WindowImpl::ClearUserData() { 278 if (::IsWindow(hwnd_)) 279 gfx::SetWindowUserData(hwnd_, NULL); 280 } 281 282 // static 283 LRESULT CALLBACK WindowImpl::WndProc(HWND hwnd, 284 UINT message, 285 WPARAM w_param, 286 LPARAM l_param) { 287 if (message == WM_NCCREATE) { 288 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(l_param); 289 WindowImpl* window = reinterpret_cast<WindowImpl*>(cs->lpCreateParams); 290 DCHECK(window); 291 gfx::SetWindowUserData(hwnd, window); 292 window->hwnd_ = hwnd; 293 window->got_create_ = true; 294 if (hwnd) 295 window->got_valid_hwnd_ = true; 296 return TRUE; 297 } 298 299 WindowImpl* window = reinterpret_cast<WindowImpl*>(GetWindowUserData(hwnd)); 300 if (!window) 301 return 0; 302 303 return window->OnWndProc(message, w_param, l_param); 304 } 305 306 ATOM WindowImpl::GetWindowClassAtom() { 307 HICON icon = GetDefaultWindowIcon(); 308 ClassInfo class_info(initial_class_style(), icon); 309 return ClassRegistrar::GetInstance()->RetrieveClassAtom(class_info); 310 } 311 312 } // namespace gfx 313