Home | History | Annotate | Download | only in win
      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