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