hwnd_message_handler.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/views/win/hwnd_message_handler.h"
6
7#include <dwmapi.h>
8#include <shellapi.h>
9
10#include "base/bind.h"
11#include "base/debug/trace_event.h"
12#include "base/win/windows_version.h"
13#include "ui/base/events/event.h"
14#include "ui/base/events/event_utils.h"
15#include "ui/base/gestures/gesture_sequence.h"
16#include "ui/base/keycodes/keyboard_code_conversion_win.h"
17#include "ui/base/win/dpi.h"
18#include "ui/base/win/hwnd_util.h"
19#include "ui/base/win/mouse_wheel_util.h"
20#include "ui/base/win/shell.h"
21#include "ui/base/win/touch_input.h"
22#include "ui/gfx/canvas.h"
23#include "ui/gfx/canvas_skia_paint.h"
24#include "ui/gfx/icon_util.h"
25#include "ui/gfx/insets.h"
26#include "ui/gfx/path.h"
27#include "ui/gfx/path_win.h"
28#include "ui/gfx/screen.h"
29#include "ui/native_theme/native_theme_win.h"
30#include "ui/views/views_delegate.h"
31#include "ui/views/widget/monitor_win.h"
32#include "ui/views/widget/native_widget_win.h"
33#include "ui/views/widget/widget_hwnd_utils.h"
34#include "ui/views/win/appbar.h"
35#include "ui/views/win/fullscreen_handler.h"
36#include "ui/views/win/hwnd_message_handler_delegate.h"
37#include "ui/views/win/scoped_fullscreen_visibility.h"
38
39#if !defined(USE_AURA)
40#include "ui/views/accessibility/native_view_accessibility_win.h"
41#include "ui/views/widget/child_window_message_processor.h"
42#endif
43
44namespace views {
45namespace {
46
47// MoveLoopMouseWatcher is used to determine if the user canceled or completed a
48// move. win32 doesn't appear to offer a way to determine the result of a move,
49// so we install hooks to determine if we got a mouse up and assume the move
50// completed.
51class MoveLoopMouseWatcher {
52 public:
53  explicit MoveLoopMouseWatcher(HWNDMessageHandler* host);
54  ~MoveLoopMouseWatcher();
55
56  // Returns true if the mouse is up, or if we couldn't install the hook.
57  bool got_mouse_up() const { return got_mouse_up_; }
58
59 private:
60  // Instance that owns the hook. We only allow one instance to hook the mouse
61  // at a time.
62  static MoveLoopMouseWatcher* instance_;
63
64  // Key and mouse callbacks from the hook.
65  static LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param);
66  static LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param);
67
68  void Unhook();
69
70  // HWNDMessageHandler that created us.
71  HWNDMessageHandler* host_;
72
73  // Did we get a mouse up?
74  bool got_mouse_up_;
75
76  // Hook identifiers.
77  HHOOK mouse_hook_;
78  HHOOK key_hook_;
79
80  DISALLOW_COPY_AND_ASSIGN(MoveLoopMouseWatcher);
81};
82
83// static
84MoveLoopMouseWatcher* MoveLoopMouseWatcher::instance_ = NULL;
85
86MoveLoopMouseWatcher::MoveLoopMouseWatcher(HWNDMessageHandler* host)
87    : host_(host),
88      got_mouse_up_(false),
89      mouse_hook_(NULL),
90      key_hook_(NULL) {
91  // Only one instance can be active at a time.
92  if (instance_)
93    instance_->Unhook();
94
95  mouse_hook_ = SetWindowsHookEx(
96      WH_MOUSE, &MouseHook, NULL, GetCurrentThreadId());
97  if (mouse_hook_) {
98    instance_ = this;
99    // We don't care if setting the key hook succeeded.
100    key_hook_ = SetWindowsHookEx(
101        WH_KEYBOARD, &KeyHook, NULL, GetCurrentThreadId());
102  }
103  if (instance_ != this) {
104    // Failed installation. Assume we got a mouse up in this case, otherwise
105    // we'll think all drags were canceled.
106    got_mouse_up_ = true;
107  }
108}
109
110MoveLoopMouseWatcher::~MoveLoopMouseWatcher() {
111  Unhook();
112}
113
114void MoveLoopMouseWatcher::Unhook() {
115  if (instance_ != this)
116    return;
117
118  DCHECK(mouse_hook_);
119  UnhookWindowsHookEx(mouse_hook_);
120  if (key_hook_)
121    UnhookWindowsHookEx(key_hook_);
122  key_hook_ = NULL;
123  mouse_hook_ = NULL;
124  instance_ = NULL;
125}
126
127// static
128LRESULT CALLBACK MoveLoopMouseWatcher::MouseHook(int n_code,
129                                                 WPARAM w_param,
130                                                 LPARAM l_param) {
131  DCHECK(instance_);
132  if (n_code == HC_ACTION && w_param == WM_LBUTTONUP)
133    instance_->got_mouse_up_ = true;
134  return CallNextHookEx(instance_->mouse_hook_, n_code, w_param, l_param);
135}
136
137// static
138LRESULT CALLBACK MoveLoopMouseWatcher::KeyHook(int n_code,
139                                               WPARAM w_param,
140                                               LPARAM l_param) {
141  if (n_code == HC_ACTION && w_param == VK_ESCAPE) {
142    if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
143      int value = TRUE;
144      HRESULT result = DwmSetWindowAttribute(
145          instance_->host_->hwnd(),
146          DWMWA_TRANSITIONS_FORCEDISABLED,
147          &value,
148          sizeof(value));
149    }
150    // Hide the window on escape, otherwise the window is visibly going to snap
151    // back to the original location before we close it.
152    // This behavior is specific to tab dragging, in that we generally wouldn't
153    // want this functionality if we have other consumers using this API.
154    instance_->host_->Hide();
155  }
156  return CallNextHookEx(instance_->key_hook_, n_code, w_param, l_param);
157}
158
159// Called from OnNCActivate.
160BOOL CALLBACK EnumChildWindowsForRedraw(HWND hwnd, LPARAM lparam) {
161  DWORD process_id;
162  GetWindowThreadProcessId(hwnd, &process_id);
163  int flags = RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME;
164  if (process_id == GetCurrentProcessId())
165    flags |= RDW_UPDATENOW;
166  RedrawWindow(hwnd, NULL, NULL, flags);
167  return TRUE;
168}
169
170bool GetMonitorAndRects(const RECT& rect,
171                        HMONITOR* monitor,
172                        gfx::Rect* monitor_rect,
173                        gfx::Rect* work_area) {
174  DCHECK(monitor);
175  DCHECK(monitor_rect);
176  DCHECK(work_area);
177  *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
178  if (!*monitor)
179    return false;
180  MONITORINFO monitor_info = { 0 };
181  monitor_info.cbSize = sizeof(monitor_info);
182  GetMonitorInfo(*monitor, &monitor_info);
183  *monitor_rect = gfx::Rect(monitor_info.rcMonitor);
184  *work_area = gfx::Rect(monitor_info.rcWork);
185  return true;
186}
187
188struct FindOwnedWindowsData {
189  HWND window;
190  std::vector<Widget*> owned_widgets;
191};
192
193BOOL CALLBACK FindOwnedWindowsCallback(HWND hwnd, LPARAM param) {
194  // TODO(beng): resolve wrt aura.
195#if !defined(USE_AURA)
196  FindOwnedWindowsData* data = reinterpret_cast<FindOwnedWindowsData*>(param);
197  if (GetWindow(hwnd, GW_OWNER) == data->window) {
198    Widget* widget = Widget::GetWidgetForNativeView(hwnd);
199    if (widget)
200      data->owned_widgets.push_back(widget);
201  }
202#endif
203  return TRUE;
204}
205
206// Enables or disables the menu item for the specified command and menu.
207void EnableMenuItemByCommand(HMENU menu, UINT command, bool enabled) {
208  UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
209  EnableMenuItem(menu, command, flags);
210}
211
212// Callback used to notify child windows that the top level window received a
213// DWMCompositionChanged message.
214BOOL CALLBACK SendDwmCompositionChanged(HWND window, LPARAM param) {
215  SendMessage(window, WM_DWMCOMPOSITIONCHANGED, 0, 0);
216  return TRUE;
217}
218
219// See comments in OnNCPaint() for details of this struct.
220struct ClipState {
221  // The window being painted.
222  HWND parent;
223
224  // DC painting to.
225  HDC dc;
226
227  // Origin of the window in terms of the screen.
228  int x;
229  int y;
230};
231
232// See comments in OnNCPaint() for details of this function.
233static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) {
234  ClipState* clip_state = reinterpret_cast<ClipState*>(param);
235  if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) {
236    RECT bounds;
237    GetWindowRect(window, &bounds);
238    ExcludeClipRect(clip_state->dc,
239      bounds.left - clip_state->x,
240      bounds.top - clip_state->y,
241      bounds.right - clip_state->x,
242      bounds.bottom - clip_state->y);
243  }
244  return TRUE;
245}
246
247#if !defined(USE_AURA)
248
249// Get the source HWND of the specified message. Depending on the message, the
250// source HWND is encoded in either the WPARAM or the LPARAM value.
251HWND GetControlHWNDForMessage(UINT message, WPARAM w_param, LPARAM l_param) {
252  // Each of the following messages can be sent by a child HWND and must be
253  // forwarded to its associated NativeControlWin for handling.
254  switch (message) {
255    case WM_NOTIFY:
256      return reinterpret_cast<NMHDR*>(l_param)->hwndFrom;
257    case WM_COMMAND:
258      return reinterpret_cast<HWND>(l_param);
259    case WM_CONTEXTMENU:
260      return reinterpret_cast<HWND>(w_param);
261    case WM_CTLCOLORBTN:
262    case WM_CTLCOLORSTATIC:
263      return reinterpret_cast<HWND>(l_param);
264  }
265  return NULL;
266}
267
268// Some messages may be sent to us by a child HWND. If this is the case, this
269// function will forward those messages on to the object associated with the
270// source HWND and return true, in which case the window procedure must not do
271// any further processing of the message. If there is no associated
272// ChildWindowMessageProcessor, the return value will be false and the WndProc
273// can continue processing the message normally.  |l_result| contains the result
274// of the message processing by the control and must be returned by the WndProc
275// if the return value is true.
276bool ProcessChildWindowMessage(UINT message,
277                               WPARAM w_param,
278                               LPARAM l_param,
279                               LRESULT* l_result) {
280  *l_result = 0;
281
282  HWND control_hwnd = GetControlHWNDForMessage(message, w_param, l_param);
283  if (IsWindow(control_hwnd)) {
284    ChildWindowMessageProcessor* processor =
285        ChildWindowMessageProcessor::Get(control_hwnd);
286    if (processor)
287      return processor->ProcessMessage(message, w_param, l_param, l_result);
288  }
289
290  return false;
291}
292
293#endif
294
295// The thickness of an auto-hide taskbar in pixels.
296const int kAutoHideTaskbarThicknessPx = 2;
297
298// For windows with the standard frame removed, the client area needs to be
299// different from the window area to avoid a "feature" in Windows's handling of
300// WM_NCCALCSIZE data. See the comment near the bottom of GetClientAreaInsets
301// for more details.
302const int kClientAreaBottomInsetHack = -1;
303
304}  // namespace
305
306// A scoping class that prevents a window from being able to redraw in response
307// to invalidations that may occur within it for the lifetime of the object.
308//
309// Why would we want such a thing? Well, it turns out Windows has some
310// "unorthodox" behavior when it comes to painting its non-client areas.
311// Occasionally, Windows will paint portions of the default non-client area
312// right over the top of the custom frame. This is not simply fixed by handling
313// WM_NCPAINT/WM_PAINT, with some investigation it turns out that this
314// rendering is being done *inside* the default implementation of some message
315// handlers and functions:
316//  . WM_SETTEXT
317//  . WM_SETICON
318//  . WM_NCLBUTTONDOWN
319//  . EnableMenuItem, called from our WM_INITMENU handler
320// The solution is to handle these messages and call DefWindowProc ourselves,
321// but prevent the window from being able to update itself for the duration of
322// the call. We do this with this class, which automatically calls its
323// associated Window's lock and unlock functions as it is created and destroyed.
324// See documentation in those methods for the technique used.
325//
326// The lock only has an effect if the window was visible upon lock creation, as
327// it doesn't guard against direct visiblility changes, and multiple locks may
328// exist simultaneously to handle certain nested Windows messages.
329//
330// IMPORTANT: Do not use this scoping object for large scopes or periods of
331//            time! IT WILL PREVENT THE WINDOW FROM BEING REDRAWN! (duh).
332//
333// I would love to hear Raymond Chen's explanation for all this. And maybe a
334// list of other messages that this applies to ;-)
335class HWNDMessageHandler::ScopedRedrawLock {
336 public:
337  explicit ScopedRedrawLock(HWNDMessageHandler* owner)
338    : owner_(owner),
339      hwnd_(owner_->hwnd()),
340      was_visible_(owner_->IsVisible()),
341      cancel_unlock_(false),
342      force_(!(GetWindowLong(hwnd_, GWL_STYLE) & WS_CAPTION)) {
343    if (was_visible_ && ::IsWindow(hwnd_))
344      owner_->LockUpdates(force_);
345  }
346
347  ~ScopedRedrawLock() {
348    if (!cancel_unlock_ && was_visible_ && ::IsWindow(hwnd_))
349      owner_->UnlockUpdates(force_);
350  }
351
352  // Cancel the unlock operation, call this if the Widget is being destroyed.
353  void CancelUnlockOperation() { cancel_unlock_ = true; }
354
355 private:
356  // The owner having its style changed.
357  HWNDMessageHandler* owner_;
358  // The owner's HWND, cached to avoid action after window destruction.
359  HWND hwnd_;
360  // Records the HWND visibility at the time of creation.
361  bool was_visible_;
362  // A flag indicating that the unlock operation was canceled.
363  bool cancel_unlock_;
364  // If true, perform the redraw lock regardless of Aero state.
365  bool force_;
366
367  DISALLOW_COPY_AND_ASSIGN(ScopedRedrawLock);
368};
369
370////////////////////////////////////////////////////////////////////////////////
371// HWNDMessageHandler, public:
372
373HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate)
374    : delegate_(delegate),
375      fullscreen_handler_(new FullscreenHandler),
376      close_widget_factory_(this),
377      remove_standard_frame_(false),
378      use_system_default_icon_(false),
379      restore_focus_when_enabled_(false),
380      restored_enabled_(false),
381      previous_cursor_(NULL),
382      active_mouse_tracking_flags_(0),
383      is_right_mouse_pressed_on_caption_(false),
384      lock_updates_count_(0),
385      destroyed_(NULL),
386      ignore_window_pos_changes_(false),
387      ignore_pos_changes_factory_(this),
388      last_monitor_(NULL),
389      use_layered_buffer_(false),
390      layered_alpha_(255),
391      paint_layered_window_factory_(this),
392      can_update_layered_window_(true),
393      is_first_nccalc_(true),
394      autohide_factory_(this) {
395}
396
397HWNDMessageHandler::~HWNDMessageHandler() {
398  delegate_ = NULL;
399  if (destroyed_ != NULL)
400    *destroyed_ = true;
401  // Prevent calls back into this class via WNDPROC now that we've been
402  // destroyed.
403  ClearUserData();
404}
405
406void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) {
407  TRACE_EVENT0("views", "HWNDMessageHandler::Init");
408  GetMonitorAndRects(bounds.ToRECT(), &last_monitor_, &last_monitor_rect_,
409                     &last_work_area_);
410
411  // Create the window.
412  WindowImpl::Init(parent, bounds);
413}
414
415void HWNDMessageHandler::InitModalType(ui::ModalType modal_type) {
416  if (modal_type == ui::MODAL_TYPE_NONE)
417    return;
418  // We implement modality by crawling up the hierarchy of windows starting
419  // at the owner, disabling all of them so that they don't receive input
420  // messages.
421  HWND start = ::GetWindow(hwnd(), GW_OWNER);
422  while (start) {
423    ::EnableWindow(start, FALSE);
424    start = ::GetParent(start);
425  }
426}
427
428void HWNDMessageHandler::Close() {
429  if (!IsWindow(hwnd()))
430    return;  // No need to do anything.
431
432  // Let's hide ourselves right away.
433  Hide();
434
435  // Modal dialog windows disable their owner windows; re-enable them now so
436  // they can activate as foreground windows upon this window's destruction.
437  RestoreEnabledIfNecessary();
438
439  if (!close_widget_factory_.HasWeakPtrs()) {
440    // And we delay the close so that if we are called from an ATL callback,
441    // we don't destroy the window before the callback returned (as the caller
442    // may delete ourselves on destroy and the ATL callback would still
443    // dereference us when the callback returns).
444    base::MessageLoop::current()->PostTask(
445        FROM_HERE,
446        base::Bind(&HWNDMessageHandler::CloseNow,
447                   close_widget_factory_.GetWeakPtr()));
448  }
449}
450
451void HWNDMessageHandler::CloseNow() {
452  // We may already have been destroyed if the selection resulted in a tab
453  // switch which will have reactivated the browser window and closed us, so
454  // we need to check to see if we're still a window before trying to destroy
455  // ourself.
456  if (IsWindow(hwnd()))
457    DestroyWindow(hwnd());
458}
459
460gfx::Rect HWNDMessageHandler::GetWindowBoundsInScreen() const {
461  RECT r;
462  GetWindowRect(hwnd(), &r);
463  return gfx::Rect(r);
464}
465
466gfx::Rect HWNDMessageHandler::GetClientAreaBoundsInScreen() const {
467  RECT r;
468  GetClientRect(hwnd(), &r);
469  POINT point = { r.left, r.top };
470  ClientToScreen(hwnd(), &point);
471  return gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top);
472}
473
474gfx::Rect HWNDMessageHandler::GetRestoredBounds() const {
475  // If we're in fullscreen mode, we've changed the normal bounds to the monitor
476  // rect, so return the saved bounds instead.
477  if (fullscreen_handler_->fullscreen())
478    return fullscreen_handler_->GetRestoreBounds();
479
480  gfx::Rect bounds;
481  GetWindowPlacement(&bounds, NULL);
482  return bounds;
483}
484
485void HWNDMessageHandler::GetWindowPlacement(
486    gfx::Rect* bounds,
487    ui::WindowShowState* show_state) const {
488  WINDOWPLACEMENT wp;
489  wp.length = sizeof(wp);
490  const bool succeeded = !!::GetWindowPlacement(hwnd(), &wp);
491  DCHECK(succeeded);
492
493  if (bounds != NULL) {
494    if (wp.showCmd == SW_SHOWNORMAL) {
495      // GetWindowPlacement can return misleading position if a normalized
496      // window was resized using Aero Snap feature (see comment 9 in bug
497      // 36421). As a workaround, using GetWindowRect for normalized windows.
498      const bool succeeded = GetWindowRect(hwnd(), &wp.rcNormalPosition) != 0;
499      DCHECK(succeeded);
500
501      *bounds = gfx::Rect(wp.rcNormalPosition);
502    } else {
503      MONITORINFO mi;
504      mi.cbSize = sizeof(mi);
505      const bool succeeded = GetMonitorInfo(
506          MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST), &mi) != 0;
507      DCHECK(succeeded);
508
509      *bounds = gfx::Rect(wp.rcNormalPosition);
510      // Convert normal position from workarea coordinates to screen
511      // coordinates.
512      bounds->Offset(mi.rcWork.left - mi.rcMonitor.left,
513                     mi.rcWork.top - mi.rcMonitor.top);
514    }
515  }
516
517  if (show_state) {
518    if (wp.showCmd == SW_SHOWMAXIMIZED)
519      *show_state = ui::SHOW_STATE_MAXIMIZED;
520    else if (wp.showCmd == SW_SHOWMINIMIZED)
521      *show_state = ui::SHOW_STATE_MINIMIZED;
522    else
523      *show_state = ui::SHOW_STATE_NORMAL;
524  }
525}
526
527void HWNDMessageHandler::SetBounds(const gfx::Rect& bounds_in_pixels) {
528  LONG style = GetWindowLong(hwnd(), GWL_STYLE);
529  if (style & WS_MAXIMIZE)
530    SetWindowLong(hwnd(), GWL_STYLE, style & ~WS_MAXIMIZE);
531  SetWindowPos(hwnd(), NULL, bounds_in_pixels.x(), bounds_in_pixels.y(),
532               bounds_in_pixels.width(), bounds_in_pixels.height(),
533               SWP_NOACTIVATE | SWP_NOZORDER);
534}
535
536void HWNDMessageHandler::SetSize(const gfx::Size& size) {
537  SetWindowPos(hwnd(), NULL, 0, 0, size.width(), size.height(),
538               SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
539}
540
541void HWNDMessageHandler::CenterWindow(const gfx::Size& size) {
542  HWND parent = GetParent(hwnd());
543  if (!IsWindow(hwnd()))
544    parent = ::GetWindow(hwnd(), GW_OWNER);
545  ui::CenterAndSizeWindow(parent, hwnd(), size);
546}
547
548void HWNDMessageHandler::SetRegion(HRGN region) {
549  SetWindowRgn(hwnd(), region, TRUE);
550}
551
552void HWNDMessageHandler::StackAbove(HWND other_hwnd) {
553  SetWindowPos(hwnd(), other_hwnd, 0, 0, 0, 0,
554               SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
555}
556
557void HWNDMessageHandler::StackAtTop() {
558  SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0,
559               SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
560}
561
562void HWNDMessageHandler::Show() {
563  if (IsWindow(hwnd()))
564    ShowWindowWithState(ui::SHOW_STATE_INACTIVE);
565}
566
567void HWNDMessageHandler::ShowWindowWithState(ui::WindowShowState show_state) {
568  TRACE_EVENT0("views", "HWNDMessageHandler::ShowWindowWithState");
569  DWORD native_show_state;
570  switch (show_state) {
571    case ui::SHOW_STATE_INACTIVE:
572      native_show_state = SW_SHOWNOACTIVATE;
573      break;
574    case ui::SHOW_STATE_MAXIMIZED:
575      native_show_state = SW_SHOWMAXIMIZED;
576      break;
577    case ui::SHOW_STATE_MINIMIZED:
578      native_show_state = SW_SHOWMINIMIZED;
579      break;
580    default:
581      native_show_state = delegate_->GetInitialShowState();
582      break;
583  }
584  Show(native_show_state);
585}
586
587void HWNDMessageHandler::Show(int show_state) {
588  ShowWindow(hwnd(), show_state);
589  // When launched from certain programs like bash and Windows Live Messenger,
590  // show_state is set to SW_HIDE, so we need to correct that condition. We
591  // don't just change show_state to SW_SHOWNORMAL because MSDN says we must
592  // always first call ShowWindow with the specified value from STARTUPINFO,
593  // otherwise all future ShowWindow calls will be ignored (!!#@@#!). Instead,
594  // we call ShowWindow again in this case.
595  if (show_state == SW_HIDE) {
596    show_state = SW_SHOWNORMAL;
597    ShowWindow(hwnd(), show_state);
598  }
599
600  // We need to explicitly activate the window if we've been shown with a state
601  // that should activate, because if we're opened from a desktop shortcut while
602  // an existing window is already running it doesn't seem to be enough to use
603  // one of these flags to activate the window.
604  if (show_state == SW_SHOWNORMAL || show_state == SW_SHOWMAXIMIZED)
605    Activate();
606
607  if (!delegate_->HandleInitialFocus())
608    SetInitialFocus();
609}
610
611void HWNDMessageHandler::ShowMaximizedWithBounds(const gfx::Rect& bounds) {
612  WINDOWPLACEMENT placement = { 0 };
613  placement.length = sizeof(WINDOWPLACEMENT);
614  placement.showCmd = SW_SHOWMAXIMIZED;
615  placement.rcNormalPosition = bounds.ToRECT();
616  SetWindowPlacement(hwnd(), &placement);
617}
618
619void HWNDMessageHandler::Hide() {
620  if (IsWindow(hwnd())) {
621    // NOTE: Be careful not to activate any windows here (for example, calling
622    // ShowWindow(SW_HIDE) will automatically activate another window).  This
623    // code can be called while a window is being deactivated, and activating
624    // another window will screw up the activation that is already in progress.
625    SetWindowPos(hwnd(), NULL, 0, 0, 0, 0,
626                 SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
627                 SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
628
629    if (!GetParent(hwnd()))
630      NotifyOwnedWindowsParentClosing();
631  }
632}
633
634void HWNDMessageHandler::Maximize() {
635  ExecuteSystemMenuCommand(SC_MAXIMIZE);
636}
637
638void HWNDMessageHandler::Minimize() {
639  ExecuteSystemMenuCommand(SC_MINIMIZE);
640  delegate_->HandleNativeBlur(NULL);
641}
642
643void HWNDMessageHandler::Restore() {
644  ExecuteSystemMenuCommand(SC_RESTORE);
645}
646
647void HWNDMessageHandler::Activate() {
648  if (IsMinimized())
649    ::ShowWindow(hwnd(), SW_RESTORE);
650  ::SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
651  SetForegroundWindow(hwnd());
652}
653
654void HWNDMessageHandler::Deactivate() {
655  HWND next_hwnd = ::GetNextWindow(hwnd(), GW_HWNDNEXT);
656  if (next_hwnd)
657    ::SetForegroundWindow(next_hwnd);
658}
659
660void HWNDMessageHandler::SetAlwaysOnTop(bool on_top) {
661  ::SetWindowPos(hwnd(), on_top ? HWND_TOPMOST : HWND_NOTOPMOST,
662                 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
663}
664
665bool HWNDMessageHandler::IsVisible() const {
666  return !!::IsWindowVisible(hwnd());
667}
668
669bool HWNDMessageHandler::IsActive() const {
670  return GetActiveWindow() == hwnd();
671}
672
673bool HWNDMessageHandler::IsMinimized() const {
674  return !!::IsIconic(hwnd());
675}
676
677bool HWNDMessageHandler::IsMaximized() const {
678  return !!::IsZoomed(hwnd());
679}
680
681bool HWNDMessageHandler::RunMoveLoop(const gfx::Vector2d& drag_offset) {
682  ReleaseCapture();
683  MoveLoopMouseWatcher watcher(this);
684  SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002, GetMessagePos());
685  // Windows doesn't appear to offer a way to determine whether the user
686  // canceled the move or not. We assume if the user released the mouse it was
687  // successful.
688  return watcher.got_mouse_up();
689}
690
691void HWNDMessageHandler::EndMoveLoop() {
692  SendMessage(hwnd(), WM_CANCELMODE, 0, 0);
693}
694
695void HWNDMessageHandler::SendFrameChanged() {
696  SetWindowPos(hwnd(), NULL, 0, 0, 0, 0,
697      SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS |
698      SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREPOSITION |
699      SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER);
700}
701
702void HWNDMessageHandler::FlashFrame(bool flash) {
703  FLASHWINFO fwi;
704  fwi.cbSize = sizeof(fwi);
705  fwi.hwnd = hwnd();
706  if (flash) {
707    fwi.dwFlags = FLASHW_ALL;
708    fwi.uCount = 4;
709    fwi.dwTimeout = 0;
710  } else {
711    fwi.dwFlags = FLASHW_STOP;
712  }
713  FlashWindowEx(&fwi);
714}
715
716void HWNDMessageHandler::ClearNativeFocus() {
717  ::SetFocus(hwnd());
718}
719
720void HWNDMessageHandler::SetCapture() {
721  DCHECK(!HasCapture());
722  ::SetCapture(hwnd());
723}
724
725void HWNDMessageHandler::ReleaseCapture() {
726  if (HasCapture())
727    ::ReleaseCapture();
728}
729
730bool HWNDMessageHandler::HasCapture() const {
731  return ::GetCapture() == hwnd();
732}
733
734void HWNDMessageHandler::SetVisibilityChangedAnimationsEnabled(bool enabled) {
735  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
736    int dwm_value = enabled ? FALSE : TRUE;
737    DwmSetWindowAttribute(
738        hwnd(), DWMWA_TRANSITIONS_FORCEDISABLED, &dwm_value, sizeof(dwm_value));
739  }
740}
741
742void HWNDMessageHandler::SetTitle(const string16& title) {
743  SetWindowText(hwnd(), title.c_str());
744}
745
746void HWNDMessageHandler::SetCursor(HCURSOR cursor) {
747  if (cursor) {
748    previous_cursor_ = ::SetCursor(cursor);
749  } else if (previous_cursor_) {
750    ::SetCursor(previous_cursor_);
751    previous_cursor_ = NULL;
752  }
753}
754
755void HWNDMessageHandler::FrameTypeChanged() {
756  // Called when the frame type could possibly be changing (theme change or
757  // DWM composition change).
758  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
759    // We need to toggle the rendering policy of the DWM/glass frame as we
760    // change from opaque to glass. "Non client rendering enabled" means that
761    // the DWM's glass non-client rendering is enabled, which is why
762    // DWMNCRP_ENABLED is used for the native frame case. _DISABLED means the
763    // DWM doesn't render glass, and so is used in the custom frame case.
764    DWMNCRENDERINGPOLICY policy = !delegate_->IsUsingCustomFrame() ?
765        DWMNCRP_ENABLED : DWMNCRP_DISABLED;
766    DwmSetWindowAttribute(hwnd(), DWMWA_NCRENDERING_POLICY,
767                          &policy, sizeof(DWMNCRENDERINGPOLICY));
768  }
769
770  ResetWindowRegion(true);
771
772  // The non-client view needs to update too.
773  delegate_->HandleFrameChanged();
774
775  // WM_DWMCOMPOSITIONCHANGED is only sent to top level windows, however we want
776  // to notify our children too, since we can have MDI child windows who need to
777  // update their appearance.
778  EnumChildWindows(hwnd(), &SendDwmCompositionChanged, NULL);
779}
780
781void HWNDMessageHandler::SchedulePaintInRect(const gfx::Rect& rect) {
782  if (use_layered_buffer_) {
783    // We must update the back-buffer immediately, since Windows' handling of
784    // invalid rects is somewhat mysterious.
785    invalid_rect_.Union(rect);
786
787    // In some situations, such as drag and drop, when Windows itself runs a
788    // nested message loop our message loop appears to be starved and we don't
789    // receive calls to DidProcessMessage(). This only seems to affect layered
790    // windows, so we schedule a redraw manually using a task, since those never
791    // seem to be starved. Also, wtf.
792    if (!paint_layered_window_factory_.HasWeakPtrs()) {
793      base::MessageLoop::current()->PostTask(
794          FROM_HERE,
795          base::Bind(&HWNDMessageHandler::RedrawLayeredWindowContents,
796                     paint_layered_window_factory_.GetWeakPtr()));
797    }
798  } else {
799    // InvalidateRect() expects client coordinates.
800    RECT r = rect.ToRECT();
801    InvalidateRect(hwnd(), &r, FALSE);
802  }
803}
804
805void HWNDMessageHandler::SetOpacity(BYTE opacity) {
806  layered_alpha_ = opacity;
807}
808
809void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon,
810                                        const gfx::ImageSkia& app_icon) {
811  if (!window_icon.isNull()) {
812    HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(
813        *window_icon.bitmap());
814    // We need to make sure to destroy the previous icon, otherwise we'll leak
815    // these GDI objects until we crash!
816    HICON old_icon = reinterpret_cast<HICON>(
817        SendMessage(hwnd(), WM_SETICON, ICON_SMALL,
818                    reinterpret_cast<LPARAM>(windows_icon)));
819    if (old_icon)
820      DestroyIcon(old_icon);
821  }
822  if (!app_icon.isNull()) {
823    HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(*app_icon.bitmap());
824    HICON old_icon = reinterpret_cast<HICON>(
825        SendMessage(hwnd(), WM_SETICON, ICON_BIG,
826                    reinterpret_cast<LPARAM>(windows_icon)));
827    if (old_icon)
828      DestroyIcon(old_icon);
829  }
830}
831
832////////////////////////////////////////////////////////////////////////////////
833// HWNDMessageHandler, InputMethodDelegate implementation:
834
835void HWNDMessageHandler::DispatchKeyEventPostIME(const ui::KeyEvent& key) {
836  SetMsgHandled(delegate_->HandleKeyEvent(key));
837}
838
839////////////////////////////////////////////////////////////////////////////////
840// HWNDMessageHandler, ui::WindowImpl overrides:
841
842HICON HWNDMessageHandler::GetDefaultWindowIcon() const {
843  if (use_system_default_icon_)
844    return NULL;
845  return ViewsDelegate::views_delegate ?
846      ViewsDelegate::views_delegate->GetDefaultWindowIcon() : NULL;
847}
848
849LRESULT HWNDMessageHandler::OnWndProc(UINT message,
850                                      WPARAM w_param,
851                                      LPARAM l_param) {
852  HWND window = hwnd();
853  LRESULT result = 0;
854
855  if (delegate_ && delegate_->PreHandleMSG(message, w_param, l_param, &result))
856    return result;
857
858#if !defined(USE_AURA)
859  // First allow messages sent by child controls to be processed directly by
860  // their associated views. If such a view is present, it will handle the
861  // message *instead of* this NativeWidgetWin.
862  if (ProcessChildWindowMessage(message, w_param, l_param, &result))
863    return result;
864#endif
865
866  // Otherwise we handle everything else.
867  if (!ProcessWindowMessage(window, message, w_param, l_param, result))
868    result = DefWindowProc(window, message, w_param, l_param);
869
870  // DefWindowProc() may have destroyed the window in a nested message loop.
871  if (!::IsWindow(window))
872    return result;
873
874  if (delegate_)
875    delegate_->PostHandleMSG(message, w_param, l_param);
876  if (message == WM_NCDESTROY) {
877    base::MessageLoopForUI::current()->RemoveObserver(this);
878    if (delegate_)
879      delegate_->HandleDestroyed();
880  }
881
882  // Only top level widget should store/restore focus.
883  if (message == WM_ACTIVATE && delegate_->CanSaveFocus())
884    PostProcessActivateMessage(LOWORD(w_param));
885  if (message == WM_ENABLE && restore_focus_when_enabled_) {
886    // This path should be executed only for top level as
887    // restore_focus_when_enabled_ is set in PostProcessActivateMessage.
888    DCHECK(delegate_->CanSaveFocus());
889    restore_focus_when_enabled_ = false;
890    delegate_->RestoreFocusOnEnable();
891  }
892  return result;
893}
894
895////////////////////////////////////////////////////////////////////////////////
896// HWNDMessageHandler, MessageLoopForUI::Observer implementation:
897
898base::EventStatus HWNDMessageHandler::WillProcessEvent(
899      const base::NativeEvent& event) {
900  return base::EVENT_CONTINUE;
901}
902
903void HWNDMessageHandler::DidProcessEvent(const base::NativeEvent& event) {
904  RedrawInvalidRect();
905}
906
907////////////////////////////////////////////////////////////////////////////////
908// HWNDMessageHandler, private:
909
910int HWNDMessageHandler::GetAppbarAutohideEdges(HMONITOR monitor) {
911  autohide_factory_.InvalidateWeakPtrs();
912  return Appbar::instance()->GetAutohideEdges(
913      monitor,
914      base::Bind(&HWNDMessageHandler::OnAppbarAutohideEdgesChanged,
915                 autohide_factory_.GetWeakPtr()));
916}
917
918void HWNDMessageHandler::OnAppbarAutohideEdgesChanged() {
919  // This triggers querying WM_NCCALCSIZE again.
920  RECT client;
921  GetWindowRect(hwnd(), &client);
922  SetWindowPos(hwnd(), NULL, client.left, client.top,
923               client.right - client.left, client.bottom - client.top,
924               SWP_FRAMECHANGED);
925}
926
927void HWNDMessageHandler::SetInitialFocus() {
928  if (!(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) &&
929      !(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) {
930    // The window does not get keyboard messages unless we focus it.
931    SetFocus(hwnd());
932  }
933}
934
935void HWNDMessageHandler::PostProcessActivateMessage(int activation_state) {
936  DCHECK(delegate_->CanSaveFocus());
937  if (WA_INACTIVE == activation_state) {
938    // We might get activated/inactivated without being enabled, so we need to
939    // clear restore_focus_when_enabled_.
940    restore_focus_when_enabled_ = false;
941    delegate_->SaveFocusOnDeactivate();
942  } else {
943    // We must restore the focus after the message has been DefProc'ed as it
944    // does set the focus to the last focused HWND.
945    // Note that if the window is not enabled, we cannot restore the focus as
946    // calling ::SetFocus on a child of the non-enabled top-window would fail.
947    // This is the case when showing a modal dialog (such as 'open file',
948    // 'print'...) from a different thread.
949    // In that case we delay the focus restoration to when the window is enabled
950    // again.
951    if (!IsWindowEnabled(hwnd())) {
952      DCHECK(!restore_focus_when_enabled_);
953      restore_focus_when_enabled_ = true;
954      return;
955    }
956    delegate_->RestoreFocusOnActivate();
957  }
958}
959
960void HWNDMessageHandler::RestoreEnabledIfNecessary() {
961  if (delegate_->IsModal() && !restored_enabled_) {
962    restored_enabled_ = true;
963    // If we were run modally, we need to undo the disabled-ness we inflicted on
964    // the owner's parent hierarchy.
965    HWND start = ::GetWindow(hwnd(), GW_OWNER);
966    while (start) {
967      ::EnableWindow(start, TRUE);
968      start = ::GetParent(start);
969    }
970  }
971}
972
973void HWNDMessageHandler::ExecuteSystemMenuCommand(int command) {
974  if (command)
975    SendMessage(hwnd(), WM_SYSCOMMAND, command, 0);
976}
977
978void HWNDMessageHandler::TrackMouseEvents(DWORD mouse_tracking_flags) {
979  // Begin tracking mouse events for this HWND so that we get WM_MOUSELEAVE
980  // when the user moves the mouse outside this HWND's bounds.
981  if (active_mouse_tracking_flags_ == 0 || mouse_tracking_flags & TME_CANCEL) {
982    if (mouse_tracking_flags & TME_CANCEL) {
983      // We're about to cancel active mouse tracking, so empty out the stored
984      // state.
985      active_mouse_tracking_flags_ = 0;
986    } else {
987      active_mouse_tracking_flags_ = mouse_tracking_flags;
988    }
989
990    TRACKMOUSEEVENT tme;
991    tme.cbSize = sizeof(tme);
992    tme.dwFlags = mouse_tracking_flags;
993    tme.hwndTrack = hwnd();
994    tme.dwHoverTime = 0;
995    TrackMouseEvent(&tme);
996  } else if (mouse_tracking_flags != active_mouse_tracking_flags_) {
997    TrackMouseEvents(active_mouse_tracking_flags_ | TME_CANCEL);
998    TrackMouseEvents(mouse_tracking_flags);
999  }
1000}
1001
1002void HWNDMessageHandler::ClientAreaSizeChanged() {
1003  RECT r = {0, 0, 0, 0};
1004  // In case of minimized window GetWindowRect can return normally unexpected
1005  // coordinates.
1006  if (!IsMinimized()) {
1007    if (delegate_->WidgetSizeIsClientSize()) {
1008      GetClientRect(hwnd(), &r);
1009      // This is needed due to a hack that works around a "feature" in
1010      // Windows's handling of WM_NCCALCSIZE. See the comment near the end of
1011      // GetClientAreaInsets for more details.
1012      if (remove_standard_frame_ && !IsMaximized())
1013        r.bottom += kClientAreaBottomInsetHack;
1014    } else {
1015      GetWindowRect(hwnd(), &r);
1016    }
1017  }
1018  gfx::Size s(std::max(0, static_cast<int>(r.right - r.left)),
1019              std::max(0, static_cast<int>(r.bottom - r.top)));
1020  delegate_->HandleClientSizeChanged(s);
1021  if (use_layered_buffer_) {
1022    layered_window_contents_.reset(
1023        new gfx::Canvas(s, ui::SCALE_FACTOR_100P, false));
1024  }
1025}
1026
1027gfx::Insets HWNDMessageHandler::GetClientAreaInsets() const {
1028  gfx::Insets insets;
1029  if (delegate_->GetClientAreaInsets(&insets))
1030    return insets;
1031  DCHECK(insets.empty());
1032
1033  // Returning an empty Insets object causes the default handling in
1034  // NativeWidgetWin::OnNCCalcSize() to be invoked.
1035  if (!delegate_->IsWidgetWindow() ||
1036      (!delegate_->IsUsingCustomFrame() && !remove_standard_frame_)) {
1037    return insets;
1038  }
1039
1040  if (IsMaximized()) {
1041    // Windows automatically adds a standard width border to all sides when a
1042    // window is maximized.
1043    int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
1044    if (remove_standard_frame_)
1045      border_thickness -= 1;
1046    return gfx::Insets(border_thickness, border_thickness, border_thickness,
1047                       border_thickness);
1048  }
1049
1050  // Returning empty insets for a window with the standard frame removed seems
1051  // to cause Windows to treat the window specially, treating black as
1052  // transparent and changing around some of the painting logic. I suspect it's
1053  // some sort of horrible backwards-compatability hack, but the upshot of it
1054  // is that if the insets are empty then in certain conditions (it seems to
1055  // be subtly related to timing), the contents of windows with the standard
1056  // frame removed will flicker to transparent during resize.
1057  //
1058  // To work around this, we increase the size of the client area by 1px
1059  // *beyond* the bottom of the window. This prevents Windows from having a
1060  // hissy fit and flashing the window incessantly during resizes, but it also
1061  // means that the client area is reported 1px larger than it really is, so
1062  // user code has to compensate by making its content shorter if it wants
1063  // everything to appear inside the window.
1064  if (remove_standard_frame_)
1065    return gfx::Insets(0, 0, IsMaximized() ? 0 : kClientAreaBottomInsetHack, 0);
1066  // This is weird, but highly essential. If we don't offset the bottom edge
1067  // of the client rect, the window client area and window area will match,
1068  // and when returning to glass rendering mode from non-glass, the client
1069  // area will not paint black as transparent. This is because (and I don't
1070  // know why) the client area goes from matching the window rect to being
1071  // something else. If the client area is not the window rect in both
1072  // modes, the blackness doesn't occur. Because of this, we need to tell
1073  // the RootView to lay out to fit the window rect, rather than the client
1074  // rect when using the opaque frame.
1075  // Note: this is only required for non-fullscreen windows. Note that
1076  // fullscreen windows are in restored state, not maximized.
1077  return gfx::Insets(0, 0, fullscreen_handler_->fullscreen() ? 0 : 1, 0);
1078}
1079
1080void HWNDMessageHandler::ResetWindowRegion(bool force) {
1081  // A native frame uses the native window region, and we don't want to mess
1082  // with it.
1083  if (!delegate_->IsUsingCustomFrame() || !delegate_->IsWidgetWindow()) {
1084    if (force)
1085      SetWindowRgn(hwnd(), NULL, TRUE);
1086    return;
1087  }
1088
1089  // Changing the window region is going to force a paint. Only change the
1090  // window region if the region really differs.
1091  HRGN current_rgn = CreateRectRgn(0, 0, 0, 0);
1092  int current_rgn_result = GetWindowRgn(hwnd(), current_rgn);
1093
1094  CRect window_rect;
1095  GetWindowRect(hwnd(), &window_rect);
1096  HRGN new_region;
1097  if (IsMaximized()) {
1098    HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST);
1099    MONITORINFO mi;
1100    mi.cbSize = sizeof mi;
1101    GetMonitorInfo(monitor, &mi);
1102    CRect work_rect = mi.rcWork;
1103    work_rect.OffsetRect(-window_rect.left, -window_rect.top);
1104    new_region = CreateRectRgnIndirect(&work_rect);
1105  } else {
1106    gfx::Path window_mask;
1107    delegate_->GetWindowMask(
1108        gfx::Size(window_rect.Width(), window_rect.Height()), &window_mask);
1109    new_region = gfx::CreateHRGNFromSkPath(window_mask);
1110  }
1111
1112  if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) {
1113    // SetWindowRgn takes ownership of the HRGN created by CreateNativeRegion.
1114    SetWindowRgn(hwnd(), new_region, TRUE);
1115  } else {
1116    DeleteObject(new_region);
1117  }
1118
1119  DeleteObject(current_rgn);
1120}
1121
1122LRESULT HWNDMessageHandler::DefWindowProcWithRedrawLock(UINT message,
1123                                                        WPARAM w_param,
1124                                                        LPARAM l_param) {
1125  ScopedRedrawLock lock(this);
1126  // The Widget and HWND can be destroyed in the call to DefWindowProc, so use
1127  // the |destroyed_| flag to avoid unlocking (and crashing) after destruction.
1128  bool destroyed = false;
1129  destroyed_ = &destroyed;
1130  LRESULT result = DefWindowProc(hwnd(), message, w_param, l_param);
1131  if (destroyed)
1132    lock.CancelUnlockOperation();
1133  else
1134    destroyed_ = NULL;
1135  return result;
1136}
1137
1138void HWNDMessageHandler::NotifyOwnedWindowsParentClosing() {
1139  FindOwnedWindowsData data;
1140  data.window = hwnd();
1141  EnumThreadWindows(GetCurrentThreadId(), FindOwnedWindowsCallback,
1142                    reinterpret_cast<LPARAM>(&data));
1143  for (size_t i = 0; i < data.owned_widgets.size(); ++i)
1144    data.owned_widgets[i]->OnOwnerClosing();
1145}
1146
1147void HWNDMessageHandler::LockUpdates(bool force) {
1148  // We skip locked updates when Aero is on for two reasons:
1149  // 1. Because it isn't necessary
1150  // 2. Because toggling the WS_VISIBLE flag may occur while the GPU process is
1151  //    attempting to present a child window's backbuffer onscreen. When these
1152  //    two actions race with one another, the child window will either flicker
1153  //    or will simply stop updating entirely.
1154  if ((force || !ui::win::IsAeroGlassEnabled()) && ++lock_updates_count_ == 1) {
1155    SetWindowLong(hwnd(), GWL_STYLE,
1156                  GetWindowLong(hwnd(), GWL_STYLE) & ~WS_VISIBLE);
1157  }
1158}
1159
1160void HWNDMessageHandler::UnlockUpdates(bool force) {
1161  if ((force || !ui::win::IsAeroGlassEnabled()) && --lock_updates_count_ <= 0) {
1162    SetWindowLong(hwnd(), GWL_STYLE,
1163                  GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE);
1164    lock_updates_count_ = 0;
1165  }
1166}
1167
1168void HWNDMessageHandler::RedrawInvalidRect() {
1169  if (!use_layered_buffer_) {
1170    RECT r = { 0, 0, 0, 0 };
1171    if (GetUpdateRect(hwnd(), &r, FALSE) && !IsRectEmpty(&r)) {
1172      RedrawWindow(hwnd(), &r, NULL,
1173                   RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN);
1174    }
1175  }
1176}
1177
1178void HWNDMessageHandler::RedrawLayeredWindowContents() {
1179  if (invalid_rect_.IsEmpty())
1180    return;
1181
1182  // We need to clip to the dirty rect ourselves.
1183  layered_window_contents_->sk_canvas()->save(SkCanvas::kClip_SaveFlag);
1184  double scale = ui::win::GetDeviceScaleFactor();
1185  layered_window_contents_->sk_canvas()->scale(
1186      SkScalar(scale),SkScalar(scale));
1187  layered_window_contents_->ClipRect(invalid_rect_);
1188  delegate_->PaintLayeredWindow(layered_window_contents_.get());
1189  layered_window_contents_->sk_canvas()->scale(
1190      SkScalar(1.0/scale),SkScalar(1.0/scale));
1191  layered_window_contents_->sk_canvas()->restore();
1192
1193  RECT wr;
1194  GetWindowRect(hwnd(), &wr);
1195  SIZE size = {wr.right - wr.left, wr.bottom - wr.top};
1196  POINT position = {wr.left, wr.top};
1197  HDC dib_dc = skia::BeginPlatformPaint(layered_window_contents_->sk_canvas());
1198  POINT zero = {0, 0};
1199  BLENDFUNCTION blend = {AC_SRC_OVER, 0, layered_alpha_, AC_SRC_ALPHA};
1200  UpdateLayeredWindow(hwnd(), NULL, &position, &size, dib_dc, &zero,
1201                      RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA);
1202  invalid_rect_.SetRect(0, 0, 0, 0);
1203  skia::EndPlatformPaint(layered_window_contents_->sk_canvas());
1204}
1205
1206// Message handlers ------------------------------------------------------------
1207
1208void HWNDMessageHandler::OnActivateApp(BOOL active, DWORD thread_id) {
1209  if (delegate_->IsWidgetWindow() && !active &&
1210      thread_id != GetCurrentThreadId()) {
1211    delegate_->HandleAppDeactivated();
1212    // Also update the native frame if it is rendering the non-client area.
1213    if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame())
1214      DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0);
1215  }
1216}
1217
1218BOOL HWNDMessageHandler::OnAppCommand(HWND window,
1219                                      short command,
1220                                      WORD device,
1221                                      int keystate) {
1222  BOOL handled = !!delegate_->HandleAppCommand(command);
1223  SetMsgHandled(handled);
1224  // Make sure to return TRUE if the event was handled or in some cases the
1225  // system will execute the default handler which can cause bugs like going
1226  // forward or back two pages instead of one.
1227  return handled;
1228}
1229
1230void HWNDMessageHandler::OnCancelMode() {
1231  delegate_->HandleCancelMode();
1232  // Need default handling, otherwise capture and other things aren't canceled.
1233  SetMsgHandled(FALSE);
1234}
1235
1236void HWNDMessageHandler::OnCaptureChanged(HWND window) {
1237  delegate_->HandleCaptureLost();
1238}
1239
1240void HWNDMessageHandler::OnClose() {
1241  delegate_->HandleClose();
1242}
1243
1244void HWNDMessageHandler::OnCommand(UINT notification_code,
1245                                   int command,
1246                                   HWND window) {
1247  // If the notification code is > 1 it means it is control specific and we
1248  // should ignore it.
1249  if (notification_code > 1 || delegate_->HandleAppCommand(command))
1250    SetMsgHandled(FALSE);
1251}
1252
1253LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) {
1254  use_layered_buffer_ = !!(window_ex_style() & WS_EX_LAYERED);
1255
1256#if defined(USE_AURA)
1257  if (window_ex_style() &  WS_EX_COMPOSITED) {
1258    if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
1259      // This is part of the magic to emulate layered windows with Aura
1260      // see the explanation elsewere when we set WS_EX_COMPOSITED style.
1261      MARGINS margins = {-1,-1,-1,-1};
1262      DwmExtendFrameIntoClientArea(hwnd(), &margins);
1263    }
1264  }
1265#endif
1266
1267  fullscreen_handler_->set_hwnd(hwnd());
1268
1269  // This message initializes the window so that focus border are shown for
1270  // windows.
1271  SendMessage(hwnd(),
1272              WM_CHANGEUISTATE,
1273              MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS),
1274              0);
1275
1276  if (remove_standard_frame_) {
1277    SetWindowLong(hwnd(), GWL_STYLE,
1278                  GetWindowLong(hwnd(), GWL_STYLE) & ~WS_CAPTION);
1279    SendFrameChanged();
1280  }
1281
1282  // Get access to a modifiable copy of the system menu.
1283  GetSystemMenu(hwnd(), false);
1284
1285  if (base::win::GetVersion() >= base::win::VERSION_WIN7)
1286    RegisterTouchWindow(hwnd(), 0);
1287
1288  // We need to allow the delegate to size its contents since the window may not
1289  // receive a size notification when its initial bounds are specified at window
1290  // creation time.
1291  ClientAreaSizeChanged();
1292
1293  // We need to add ourselves as a message loop observer so that we can repaint
1294  // aggressively if the contents of our window become invalid. Unfortunately
1295  // WM_PAINT messages are starved and we get flickery redrawing when resizing
1296  // if we do not do this.
1297  base::MessageLoopForUI::current()->AddObserver(this);
1298
1299  delegate_->HandleCreate();
1300
1301  // TODO(beng): move more of NWW::OnCreate here.
1302  return 0;
1303}
1304
1305void HWNDMessageHandler::OnDestroy() {
1306  delegate_->HandleDestroying();
1307}
1308
1309void HWNDMessageHandler::OnDisplayChange(UINT bits_per_pixel,
1310                                         const CSize& screen_size) {
1311  delegate_->HandleDisplayChange();
1312}
1313
1314LRESULT HWNDMessageHandler::OnDwmCompositionChanged(UINT msg,
1315                                                    WPARAM w_param,
1316                                                    LPARAM l_param) {
1317  if (!delegate_->IsWidgetWindow()) {
1318    SetMsgHandled(FALSE);
1319    return 0;
1320  }
1321  // For some reason, we need to hide the window while we're changing the frame
1322  // type only when we're changing it in response to WM_DWMCOMPOSITIONCHANGED.
1323  // If we don't, the client area will be filled with black. I'm suspecting
1324  // something skia-ey.
1325  // Frame type toggling caused by the user (e.g. switching theme) doesn't seem
1326  // to have this requirement.
1327  FrameTypeChanged();
1328  return 0;
1329}
1330
1331void HWNDMessageHandler::OnEnterSizeMove() {
1332  delegate_->HandleBeginWMSizeMove();
1333  SetMsgHandled(FALSE);
1334}
1335
1336LRESULT HWNDMessageHandler::OnEraseBkgnd(HDC dc) {
1337  // Needed to prevent resize flicker.
1338  return 1;
1339}
1340
1341void HWNDMessageHandler::OnExitSizeMove() {
1342  delegate_->HandleEndWMSizeMove();
1343  SetMsgHandled(FALSE);
1344}
1345
1346void HWNDMessageHandler::OnGetMinMaxInfo(MINMAXINFO* minmax_info) {
1347  gfx::Size min_window_size;
1348  gfx::Size max_window_size;
1349  delegate_->GetMinMaxSize(&min_window_size, &max_window_size);
1350
1351  // Add the native frame border size to the minimum and maximum size if the
1352  // view reports its size as the client size.
1353  if (delegate_->WidgetSizeIsClientSize()) {
1354    CRect client_rect, window_rect;
1355    GetClientRect(hwnd(), &client_rect);
1356    GetWindowRect(hwnd(), &window_rect);
1357    // Due to the client area bottom inset hack (detailed elsewhere), adjust
1358    // the reported size of the client area in the case that the standard frame
1359    // has been removed.
1360    if (remove_standard_frame_)
1361      client_rect.bottom += kClientAreaBottomInsetHack;
1362    window_rect -= client_rect;
1363    min_window_size.Enlarge(window_rect.Width(), window_rect.Height());
1364    if (!max_window_size.IsEmpty())
1365      max_window_size.Enlarge(window_rect.Width(), window_rect.Height());
1366  }
1367  minmax_info->ptMinTrackSize.x = min_window_size.width();
1368  minmax_info->ptMinTrackSize.y = min_window_size.height();
1369  if (max_window_size.width() || max_window_size.height()) {
1370    if (!max_window_size.width())
1371      max_window_size.set_width(GetSystemMetrics(SM_CXMAXTRACK));
1372    if (!max_window_size.height())
1373      max_window_size.set_height(GetSystemMetrics(SM_CYMAXTRACK));
1374    minmax_info->ptMaxTrackSize.x = max_window_size.width();
1375    minmax_info->ptMaxTrackSize.y = max_window_size.height();
1376  }
1377  SetMsgHandled(FALSE);
1378}
1379
1380LRESULT HWNDMessageHandler::OnGetObject(UINT message,
1381                                        WPARAM w_param,
1382                                        LPARAM l_param) {
1383  LRESULT reference_result = static_cast<LRESULT>(0L);
1384
1385  // Accessibility readers will send an OBJID_CLIENT message
1386  if (OBJID_CLIENT == l_param) {
1387    // Retrieve MSAA dispatch object for the root view.
1388    base::win::ScopedComPtr<IAccessible> root(
1389        delegate_->GetNativeViewAccessible());
1390
1391    // Create a reference that MSAA will marshall to the client.
1392    reference_result = LresultFromObject(IID_IAccessible, w_param,
1393        static_cast<IAccessible*>(root.Detach()));
1394  }
1395
1396  return reference_result;
1397}
1398
1399LRESULT HWNDMessageHandler::OnImeMessages(UINT message,
1400                                          WPARAM w_param,
1401                                          LPARAM l_param) {
1402  LRESULT result = 0;
1403  SetMsgHandled(delegate_->HandleIMEMessage(
1404      message, w_param, l_param, &result));
1405  return result;
1406}
1407
1408void HWNDMessageHandler::OnInitMenu(HMENU menu) {
1409  bool is_fullscreen = fullscreen_handler_->fullscreen();
1410  bool is_minimized = IsMinimized();
1411  bool is_maximized = IsMaximized();
1412  bool is_restored = !is_fullscreen && !is_minimized && !is_maximized;
1413
1414  ScopedRedrawLock lock(this);
1415  EnableMenuItemByCommand(menu, SC_RESTORE, is_minimized || is_maximized);
1416  EnableMenuItemByCommand(menu, SC_MOVE, is_restored);
1417  EnableMenuItemByCommand(menu, SC_SIZE, delegate_->CanResize() && is_restored);
1418  EnableMenuItemByCommand(menu, SC_MAXIMIZE, delegate_->CanMaximize() &&
1419                          !is_fullscreen && !is_maximized);
1420  EnableMenuItemByCommand(menu, SC_MINIMIZE, delegate_->CanMaximize() &&
1421                          !is_minimized);
1422}
1423
1424void HWNDMessageHandler::OnInputLangChange(DWORD character_set,
1425                                           HKL input_language_id) {
1426  delegate_->HandleInputLanguageChange(character_set, input_language_id);
1427}
1428
1429LRESULT HWNDMessageHandler::OnKeyEvent(UINT message,
1430                                       WPARAM w_param,
1431                                       LPARAM l_param) {
1432  MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime() };
1433  ui::KeyEvent key(msg, message == WM_CHAR);
1434  if (!delegate_->HandleUntranslatedKeyEvent(key))
1435    DispatchKeyEventPostIME(key);
1436  return 0;
1437}
1438
1439void HWNDMessageHandler::OnKillFocus(HWND focused_window) {
1440  delegate_->HandleNativeBlur(focused_window);
1441  SetMsgHandled(FALSE);
1442}
1443
1444LRESULT HWNDMessageHandler::OnMouseActivate(UINT message,
1445                                            WPARAM w_param,
1446                                            LPARAM l_param) {
1447  // TODO(beng): resolve this with the GetWindowLong() check on the subsequent
1448  //             line.
1449  if (delegate_->IsWidgetWindow())
1450    return delegate_->CanActivate() ? MA_ACTIVATE : MA_NOACTIVATEANDEAT;
1451  if (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)
1452    return MA_NOACTIVATE;
1453  SetMsgHandled(FALSE);
1454  return MA_ACTIVATE;
1455}
1456
1457LRESULT HWNDMessageHandler::OnMouseRange(UINT message,
1458                                         WPARAM w_param,
1459                                         LPARAM l_param) {
1460#if defined(USE_AURA)
1461  // We handle touch events on Windows Aura. Ignore synthesized mouse messages
1462  // from Windows.
1463  if (!touch_ids_.empty() || ui::IsMouseEventFromTouch(message))
1464    return 0;
1465#endif
1466  if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) {
1467    is_right_mouse_pressed_on_caption_ = false;
1468    ReleaseCapture();
1469    // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu()
1470    // expect screen coordinates.
1471    CPoint screen_point(l_param);
1472    MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1);
1473    w_param = SendMessage(hwnd(), WM_NCHITTEST, 0,
1474                          MAKELPARAM(screen_point.x, screen_point.y));
1475    if (w_param == HTCAPTION || w_param == HTSYSMENU) {
1476      ui::ShowSystemMenuAtPoint(hwnd(), gfx::Point(screen_point));
1477      return 0;
1478    }
1479  } else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) {
1480    switch (w_param) {
1481      case HTCLOSE:
1482      case HTMINBUTTON:
1483      case HTMAXBUTTON: {
1484        // When the mouse is pressed down in these specific non-client areas,
1485        // we need to tell the RootView to send the mouse pressed event (which
1486        // sets capture, allowing subsequent WM_LBUTTONUP (note, _not_
1487        // WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be
1488        // sent by the applicable button's ButtonListener. We _have_ to do this
1489        // way rather than letting Windows just send the syscommand itself (as
1490        // would happen if we never did this dance) because for some insane
1491        // reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed
1492        // window control button appearance, in the Windows classic style, over
1493        // our view! Ick! By handling this message we prevent Windows from
1494        // doing this undesirable thing, but that means we need to roll the
1495        // sys-command handling ourselves.
1496        // Combine |w_param| with common key state message flags.
1497        w_param |= base::win::IsCtrlPressed() ? MK_CONTROL : 0;
1498        w_param |= base::win::IsShiftPressed() ? MK_SHIFT : 0;
1499      }
1500    }
1501  } else if (message == WM_NCRBUTTONDOWN &&
1502      (w_param == HTCAPTION || w_param == HTSYSMENU)) {
1503    is_right_mouse_pressed_on_caption_ = true;
1504    // We SetCapture() to ensure we only show the menu when the button
1505    // down and up are both on the caption. Note: this causes the button up to
1506    // be WM_RBUTTONUP instead of WM_NCRBUTTONUP.
1507    SetCapture();
1508  }
1509
1510  MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime(),
1511              { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) } };
1512  ui::MouseEvent event(msg);
1513  if (!touch_ids_.empty() || ui::IsMouseEventFromTouch(message))
1514    event.set_flags(event.flags() | ui::EF_FROM_TOUCH);
1515
1516  if (!(event.flags() & ui::EF_IS_NON_CLIENT))
1517    delegate_->HandleTooltipMouseMove(message, w_param, l_param);
1518
1519  if (event.type() == ui::ET_MOUSE_MOVED && !HasCapture()) {
1520    // Windows only fires WM_MOUSELEAVE events if the application begins
1521    // "tracking" mouse events for a given HWND during WM_MOUSEMOVE events.
1522    // We need to call |TrackMouseEvents| to listen for WM_MOUSELEAVE.
1523    TrackMouseEvents((message == WM_NCMOUSEMOVE) ?
1524        TME_NONCLIENT | TME_LEAVE : TME_LEAVE);
1525  } else if (event.type() == ui::ET_MOUSE_EXITED) {
1526    // Reset our tracking flags so future mouse movement over this
1527    // NativeWidgetWin results in a new tracking session. Fall through for
1528    // OnMouseEvent.
1529    active_mouse_tracking_flags_ = 0;
1530  } else if (event.type() == ui::ET_MOUSEWHEEL) {
1531    // Reroute the mouse wheel to the window under the pointer if applicable.
1532    return (ui::RerouteMouseWheel(hwnd(), w_param, l_param) ||
1533            delegate_->HandleMouseEvent(ui::MouseWheelEvent(msg))) ? 0 : 1;
1534  }
1535
1536  bool handled = delegate_->HandleMouseEvent(event);
1537  if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU &&
1538      delegate_->IsUsingCustomFrame()) {
1539    // TODO(msw): Eliminate undesired painting, or re-evaluate this workaround.
1540    // DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we
1541    // need to call it inside a ScopedRedrawLock. This may cause other negative
1542    // side-effects (ex/ stifling non-client mouse releases).
1543    DefWindowProcWithRedrawLock(message, w_param, l_param);
1544    handled = true;
1545  }
1546
1547  SetMsgHandled(handled);
1548  return 0;
1549}
1550
1551void HWNDMessageHandler::OnMove(const CPoint& point) {
1552  delegate_->HandleMove();
1553  SetMsgHandled(FALSE);
1554}
1555
1556void HWNDMessageHandler::OnMoving(UINT param, const RECT* new_bounds) {
1557  delegate_->HandleMove();
1558}
1559
1560LRESULT HWNDMessageHandler::OnNCActivate(UINT message,
1561                                         WPARAM w_param,
1562                                         LPARAM l_param) {
1563  // Per MSDN, w_param is either TRUE or FALSE. However, MSDN also hints that:
1564  // "If the window is minimized when this message is received, the application
1565  // should pass the message to the DefWindowProc function."
1566  // It is found out that the high word of w_param might be set when the window
1567  // is minimized or restored. To handle this, w_param's high word should be
1568  // cleared before it is converted to BOOL.
1569  BOOL active = static_cast<BOOL>(LOWORD(w_param));
1570
1571  if (delegate_->CanActivate())
1572    delegate_->HandleActivationChanged(!!active);
1573
1574  if (!delegate_->IsWidgetWindow()) {
1575    SetMsgHandled(FALSE);
1576    return 0;
1577  }
1578
1579  if (!delegate_->CanActivate())
1580    return TRUE;
1581
1582  // On activation, lift any prior restriction against rendering as inactive.
1583  bool inactive_rendering_disabled = delegate_->IsInactiveRenderingDisabled();
1584  if (active && inactive_rendering_disabled)
1585    delegate_->EnableInactiveRendering();
1586
1587  if (delegate_->IsUsingCustomFrame()) {
1588    // TODO(beng, et al): Hack to redraw this window and child windows
1589    //     synchronously upon activation. Not all child windows are redrawing
1590    //     themselves leading to issues like http://crbug.com/74604
1591    //     We redraw out-of-process HWNDs asynchronously to avoid hanging the
1592    //     whole app if a child HWND belonging to a hung plugin is encountered.
1593    RedrawWindow(hwnd(), NULL, NULL,
1594                 RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW);
1595    EnumChildWindows(hwnd(), EnumChildWindowsForRedraw, NULL);
1596  }
1597
1598  // The frame may need to redraw as a result of the activation change.
1599  // We can get WM_NCACTIVATE before we're actually visible. If we're not
1600  // visible, no need to paint.
1601  if (IsVisible())
1602    delegate_->SchedulePaint();
1603
1604  // Avoid DefWindowProc non-client rendering over our custom frame on newer
1605  // Windows versions only (breaks taskbar activation indication on XP/Vista).
1606  if (delegate_->IsUsingCustomFrame() &&
1607      base::win::GetVersion() > base::win::VERSION_VISTA) {
1608    SetMsgHandled(TRUE);
1609    return TRUE;
1610  }
1611
1612  return DefWindowProcWithRedrawLock(
1613      WM_NCACTIVATE, inactive_rendering_disabled || active, 0);
1614}
1615
1616LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) {
1617  // We only override the default handling if we need to specify a custom
1618  // non-client edge width. Note that in most cases "no insets" means no
1619  // custom width, but in fullscreen mode or when the NonClientFrameView
1620  // requests it, we want a custom width of 0.
1621
1622  // Let User32 handle the first nccalcsize for captioned windows
1623  // so it updates its internal structures (specifically caption-present)
1624  // Without this Tile & Cascade windows won't work.
1625  // See http://code.google.com/p/chromium/issues/detail?id=900
1626  if (is_first_nccalc_) {
1627    is_first_nccalc_ = false;
1628    if (GetWindowLong(hwnd(), GWL_STYLE) & WS_CAPTION) {
1629      SetMsgHandled(FALSE);
1630      return 0;
1631    }
1632  }
1633
1634  gfx::Insets insets = GetClientAreaInsets();
1635  if (insets.empty() && !fullscreen_handler_->fullscreen() &&
1636      !(mode && remove_standard_frame_)) {
1637    SetMsgHandled(FALSE);
1638    return 0;
1639  }
1640
1641  RECT* client_rect = mode ?
1642      &(reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0]) :
1643      reinterpret_cast<RECT*>(l_param);
1644  client_rect->left += insets.left();
1645  client_rect->top += insets.top();
1646  client_rect->bottom -= insets.bottom();
1647  client_rect->right -= insets.right();
1648  if (IsMaximized()) {
1649    // Find all auto-hide taskbars along the screen edges and adjust in by the
1650    // thickness of the auto-hide taskbar on each such edge, so the window isn't
1651    // treated as a "fullscreen app", which would cause the taskbars to
1652    // disappear.
1653    HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONULL);
1654    if (!monitor) {
1655      // We might end up here if the window was previously minimized and the
1656      // user clicks on the taskbar button to restore it in the previously
1657      // maximized position. In that case WM_NCCALCSIZE is sent before the
1658      // window coordinates are restored to their previous values, so our
1659      // (left,top) would probably be (-32000,-32000) like all minimized
1660      // windows. So the above MonitorFromWindow call fails, but if we check
1661      // the window rect given with WM_NCCALCSIZE (which is our previous
1662      // restored window position) we will get the correct monitor handle.
1663      monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL);
1664      if (!monitor) {
1665        // This is probably an extreme case that we won't hit, but if we don't
1666        // intersect any monitor, let us not adjust the client rect since our
1667        // window will not be visible anyway.
1668        return 0;
1669      }
1670    }
1671    const int autohide_edges = GetAppbarAutohideEdges(monitor);
1672    if (autohide_edges & Appbar::EDGE_LEFT)
1673      client_rect->left += kAutoHideTaskbarThicknessPx;
1674    if (autohide_edges & Appbar::EDGE_TOP) {
1675      if (!delegate_->IsUsingCustomFrame()) {
1676        // Tricky bit.  Due to a bug in DwmDefWindowProc()'s handling of
1677        // WM_NCHITTEST, having any nonclient area atop the window causes the
1678        // caption buttons to draw onscreen but not respond to mouse
1679        // hover/clicks.
1680        // So for a taskbar at the screen top, we can't push the
1681        // client_rect->top down; instead, we move the bottom up by one pixel,
1682        // which is the smallest change we can make and still get a client area
1683        // less than the screen size. This is visibly ugly, but there seems to
1684        // be no better solution.
1685        --client_rect->bottom;
1686      } else {
1687        client_rect->top += kAutoHideTaskbarThicknessPx;
1688      }
1689    }
1690    if (autohide_edges & Appbar::EDGE_RIGHT)
1691      client_rect->right -= kAutoHideTaskbarThicknessPx;
1692    if (autohide_edges & Appbar::EDGE_BOTTOM)
1693      client_rect->bottom -= kAutoHideTaskbarThicknessPx;
1694
1695    // We cannot return WVR_REDRAW when there is nonclient area, or Windows
1696    // exhibits bugs where client pixels and child HWNDs are mispositioned by
1697    // the width/height of the upper-left nonclient area.
1698    return 0;
1699  }
1700
1701  // If the window bounds change, we're going to relayout and repaint anyway.
1702  // Returning WVR_REDRAW avoids an extra paint before that of the old client
1703  // pixels in the (now wrong) location, and thus makes actions like resizing a
1704  // window from the left edge look slightly less broken.
1705  // We special case when left or top insets are 0, since these conditions
1706  // actually require another repaint to correct the layout after glass gets
1707  // turned on and off.
1708  if (insets.left() == 0 || insets.top() == 0)
1709    return 0;
1710  return mode ? WVR_REDRAW : 0;
1711}
1712
1713LRESULT HWNDMessageHandler::OnNCHitTest(const CPoint& point) {
1714  if (!delegate_->IsWidgetWindow()) {
1715    SetMsgHandled(FALSE);
1716    return 0;
1717  }
1718
1719  // If the DWM is rendering the window controls, we need to give the DWM's
1720  // default window procedure first chance to handle hit testing.
1721  if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame()) {
1722    LRESULT result;
1723    if (DwmDefWindowProc(hwnd(), WM_NCHITTEST, 0,
1724                         MAKELPARAM(point.x, point.y), &result)) {
1725      return result;
1726    }
1727  }
1728
1729  // First, give the NonClientView a chance to test the point to see if it
1730  // provides any of the non-client area.
1731  POINT temp = point;
1732  MapWindowPoints(HWND_DESKTOP, hwnd(), &temp, 1);
1733  int component = delegate_->GetNonClientComponent(gfx::Point(temp));
1734  if (component != HTNOWHERE)
1735    return component;
1736
1737  // Otherwise, we let Windows do all the native frame non-client handling for
1738  // us.
1739  SetMsgHandled(FALSE);
1740  return 0;
1741}
1742
1743void HWNDMessageHandler::OnNCPaint(HRGN rgn) {
1744  // We only do non-client painting if we're not using the native frame.
1745  // It's required to avoid some native painting artifacts from appearing when
1746  // the window is resized.
1747  if (!delegate_->IsWidgetWindow() || !delegate_->IsUsingCustomFrame()) {
1748    SetMsgHandled(FALSE);
1749    return;
1750  }
1751
1752  // We have an NC region and need to paint it. We expand the NC region to
1753  // include the dirty region of the root view. This is done to minimize
1754  // paints.
1755  CRect window_rect;
1756  GetWindowRect(hwnd(), &window_rect);
1757
1758  gfx::Size root_view_size = delegate_->GetRootViewSize();
1759  if (gfx::Size(window_rect.Width(), window_rect.Height()) != root_view_size) {
1760    // If the size of the window differs from the size of the root view it
1761    // means we're being asked to paint before we've gotten a WM_SIZE. This can
1762    // happen when the user is interactively resizing the window. To avoid
1763    // mass flickering we don't do anything here. Once we get the WM_SIZE we'll
1764    // reset the region of the window which triggers another WM_NCPAINT and
1765    // all is well.
1766    return;
1767  }
1768
1769  CRect dirty_region;
1770  // A value of 1 indicates paint all.
1771  if (!rgn || rgn == reinterpret_cast<HRGN>(1)) {
1772    dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height());
1773  } else {
1774    RECT rgn_bounding_box;
1775    GetRgnBox(rgn, &rgn_bounding_box);
1776    if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect))
1777      return;  // Dirty region doesn't intersect window bounds, bale.
1778
1779    // rgn_bounding_box is in screen coordinates. Map it to window coordinates.
1780    OffsetRect(&dirty_region, -window_rect.left, -window_rect.top);
1781  }
1782
1783  // In theory GetDCEx should do what we want, but I couldn't get it to work.
1784  // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell
1785  // it doesn't work at all. So, instead we get the DC for the window then
1786  // manually clip out the children.
1787  HDC dc = GetWindowDC(hwnd());
1788  ClipState clip_state;
1789  clip_state.x = window_rect.left;
1790  clip_state.y = window_rect.top;
1791  clip_state.parent = hwnd();
1792  clip_state.dc = dc;
1793  EnumChildWindows(hwnd(), &ClipDCToChild,
1794                   reinterpret_cast<LPARAM>(&clip_state));
1795
1796  gfx::Rect old_paint_region = invalid_rect_;
1797  if (!old_paint_region.IsEmpty()) {
1798    // The root view has a region that needs to be painted. Include it in the
1799    // region we're going to paint.
1800
1801    CRect old_paint_region_crect = old_paint_region.ToRECT();
1802    CRect tmp = dirty_region;
1803    UnionRect(&dirty_region, &tmp, &old_paint_region_crect);
1804  }
1805
1806  SchedulePaintInRect(gfx::Rect(dirty_region));
1807
1808  // gfx::CanvasSkiaPaint's destructor does the actual painting. As such, wrap
1809  // the following in a block to force paint to occur so that we can release
1810  // the dc.
1811  if (!delegate_->HandlePaintAccelerated(gfx::Rect(dirty_region))) {
1812    gfx::CanvasSkiaPaint canvas(dc, true, dirty_region.left,
1813                                dirty_region.top, dirty_region.Width(),
1814                                dirty_region.Height());
1815    delegate_->HandlePaint(&canvas);
1816  }
1817
1818  ReleaseDC(hwnd(), dc);
1819  // When using a custom frame, we want to avoid calling DefWindowProc() since
1820  // that may render artifacts.
1821  SetMsgHandled(delegate_->IsUsingCustomFrame());
1822}
1823
1824LRESULT HWNDMessageHandler::OnNCUAHDrawCaption(UINT message,
1825                                               WPARAM w_param,
1826                                               LPARAM l_param) {
1827  // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for
1828  // an explanation about why we need to handle this message.
1829  SetMsgHandled(delegate_->IsUsingCustomFrame());
1830  return 0;
1831}
1832
1833LRESULT HWNDMessageHandler::OnNCUAHDrawFrame(UINT message,
1834                                             WPARAM w_param,
1835                                             LPARAM l_param) {
1836  // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for
1837  // an explanation about why we need to handle this message.
1838  SetMsgHandled(delegate_->IsUsingCustomFrame());
1839  return 0;
1840}
1841
1842LRESULT HWNDMessageHandler::OnNotify(int w_param, NMHDR* l_param) {
1843  LRESULT l_result = 0;
1844  SetMsgHandled(delegate_->HandleTooltipNotify(w_param, l_param, &l_result));
1845  return l_result;
1846}
1847
1848void HWNDMessageHandler::OnPaint(HDC dc) {
1849  RECT dirty_rect;
1850  // Try to paint accelerated first.
1851  if (GetUpdateRect(hwnd(), &dirty_rect, FALSE) &&
1852      !IsRectEmpty(&dirty_rect)) {
1853    if (delegate_->HandlePaintAccelerated(gfx::Rect(dirty_rect))) {
1854      ValidateRect(hwnd(), NULL);
1855    } else {
1856#if defined(USE_AURA)
1857      delegate_->HandlePaint(NULL);
1858#else
1859      scoped_ptr<gfx::CanvasPaint> canvas(
1860          gfx::CanvasPaint::CreateCanvasPaint(hwnd()));
1861      delegate_->HandlePaint(canvas->AsCanvas());
1862#endif
1863    }
1864  } else {
1865    // TODO(msw): Find a better solution for this crbug.com/93530 workaround.
1866    // Some scenarios otherwise fail to validate minimized app/popup windows.
1867    ValidateRect(hwnd(), NULL);
1868  }
1869}
1870
1871LRESULT HWNDMessageHandler::OnReflectedMessage(UINT message,
1872                                               WPARAM w_param,
1873                                               LPARAM l_param) {
1874  SetMsgHandled(FALSE);
1875  return 0;
1876}
1877
1878LRESULT HWNDMessageHandler::OnSetCursor(UINT message,
1879                                        WPARAM w_param,
1880                                        LPARAM l_param) {
1881  // Reimplement the necessary default behavior here. Calling DefWindowProc can
1882  // trigger weird non-client painting for non-glass windows with custom frames.
1883  // Using a ScopedRedrawLock to prevent caption rendering artifacts may allow
1884  // content behind this window to incorrectly paint in front of this window.
1885  // Invalidating the window to paint over either set of artifacts is not ideal.
1886  wchar_t* cursor = IDC_ARROW;
1887  switch (LOWORD(l_param)) {
1888    case HTSIZE:
1889      cursor = IDC_SIZENWSE;
1890      break;
1891    case HTLEFT:
1892    case HTRIGHT:
1893      cursor = IDC_SIZEWE;
1894      break;
1895    case HTTOP:
1896    case HTBOTTOM:
1897      cursor = IDC_SIZENS;
1898      break;
1899    case HTTOPLEFT:
1900    case HTBOTTOMRIGHT:
1901      cursor = IDC_SIZENWSE;
1902      break;
1903    case HTTOPRIGHT:
1904    case HTBOTTOMLEFT:
1905      cursor = IDC_SIZENESW;
1906      break;
1907    case HTCLIENT:
1908      // Client-area mouse events set the proper cursor from View::GetCursor.
1909      return 0;
1910    default:
1911      // Use the default value, IDC_ARROW.
1912      break;
1913  }
1914  SetCursor(LoadCursor(NULL, cursor));
1915  return 0;
1916}
1917
1918void HWNDMessageHandler::OnSetFocus(HWND last_focused_window) {
1919  delegate_->HandleNativeFocus(last_focused_window);
1920  SetMsgHandled(FALSE);
1921}
1922
1923LRESULT HWNDMessageHandler::OnSetIcon(UINT size_type, HICON new_icon) {
1924  // Use a ScopedRedrawLock to avoid weird non-client painting.
1925  return DefWindowProcWithRedrawLock(WM_SETICON, size_type,
1926                                     reinterpret_cast<LPARAM>(new_icon));
1927}
1928
1929LRESULT HWNDMessageHandler::OnSetText(const wchar_t* text) {
1930  // Use a ScopedRedrawLock to avoid weird non-client painting.
1931  return DefWindowProcWithRedrawLock(WM_SETTEXT, NULL,
1932                                     reinterpret_cast<LPARAM>(text));
1933}
1934
1935void HWNDMessageHandler::OnSettingChange(UINT flags, const wchar_t* section) {
1936  if (!GetParent(hwnd()) && (flags == SPI_SETWORKAREA) &&
1937      !delegate_->WillProcessWorkAreaChange()) {
1938    // Fire a dummy SetWindowPos() call, so we'll trip the code in
1939    // OnWindowPosChanging() below that notices work area changes.
1940    ::SetWindowPos(hwnd(), 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
1941        SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
1942    SetMsgHandled(TRUE);
1943  } else {
1944    if (flags == SPI_SETWORKAREA)
1945      delegate_->HandleWorkAreaChanged();
1946    SetMsgHandled(FALSE);
1947  }
1948}
1949
1950void HWNDMessageHandler::OnSize(UINT param, const CSize& size) {
1951  RedrawWindow(hwnd(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
1952  // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've
1953  // invoked OnSize we ensure the RootView has been laid out.
1954  ResetWindowRegion(false);
1955}
1956
1957void HWNDMessageHandler::OnSysCommand(UINT notification_code,
1958                                      const CPoint& point) {
1959  if (!delegate_->ShouldHandleSystemCommands())
1960    return;
1961
1962  // Windows uses the 4 lower order bits of |notification_code| for type-
1963  // specific information so we must exclude this when comparing.
1964  static const int sc_mask = 0xFFF0;
1965  // Ignore size/move/maximize in fullscreen mode.
1966  if (fullscreen_handler_->fullscreen() &&
1967      (((notification_code & sc_mask) == SC_SIZE) ||
1968       ((notification_code & sc_mask) == SC_MOVE) ||
1969       ((notification_code & sc_mask) == SC_MAXIMIZE)))
1970    return;
1971  if (delegate_->IsUsingCustomFrame()) {
1972    if ((notification_code & sc_mask) == SC_MINIMIZE ||
1973        (notification_code & sc_mask) == SC_MAXIMIZE ||
1974        (notification_code & sc_mask) == SC_RESTORE) {
1975      delegate_->ResetWindowControls();
1976    } else if ((notification_code & sc_mask) == SC_MOVE ||
1977               (notification_code & sc_mask) == SC_SIZE) {
1978      if (!IsVisible()) {
1979        // Circumvent ScopedRedrawLocks and force visibility before entering a
1980        // resize or move modal loop to get continuous sizing/moving feedback.
1981        SetWindowLong(hwnd(), GWL_STYLE,
1982                      GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE);
1983      }
1984    }
1985  }
1986
1987  // Handle SC_KEYMENU, which means that the user has pressed the ALT
1988  // key and released it, so we should focus the menu bar.
1989  if ((notification_code & sc_mask) == SC_KEYMENU && point.x == 0) {
1990    int modifiers = ui::EF_NONE;
1991    if (base::win::IsShiftPressed())
1992      modifiers |= ui::EF_SHIFT_DOWN;
1993    if (base::win::IsCtrlPressed())
1994      modifiers |= ui::EF_CONTROL_DOWN;
1995    // Retrieve the status of shift and control keys to prevent consuming
1996    // shift+alt keys, which are used by Windows to change input languages.
1997    ui::Accelerator accelerator(ui::KeyboardCodeForWindowsKeyCode(VK_MENU),
1998                                modifiers);
1999    delegate_->HandleAccelerator(accelerator);
2000    return;
2001  }
2002
2003  // If the delegate can't handle it, the system implementation will be called.
2004  if (!delegate_->HandleCommand(notification_code)) {
2005    DefWindowProc(hwnd(), WM_SYSCOMMAND, notification_code,
2006                  MAKELPARAM(point.x, point.y));
2007  }
2008}
2009
2010void HWNDMessageHandler::OnThemeChanged() {
2011  ui::NativeThemeWin::instance()->CloseHandles();
2012}
2013
2014LRESULT HWNDMessageHandler::OnTouchEvent(UINT message,
2015                                         WPARAM w_param,
2016                                         LPARAM l_param) {
2017  int num_points = LOWORD(w_param);
2018  scoped_ptr<TOUCHINPUT[]> input(new TOUCHINPUT[num_points]);
2019  if (ui::GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param),
2020                                   num_points, input.get(),
2021                                   sizeof(TOUCHINPUT))) {
2022    for (int i = 0; i < num_points; ++i) {
2023      ui::EventType touch_event_type = ui::ET_UNKNOWN;
2024
2025      if (input[i].dwFlags & TOUCHEVENTF_DOWN) {
2026        touch_ids_.insert(input[i].dwID);
2027        touch_event_type = ui::ET_TOUCH_PRESSED;
2028      } else if (input[i].dwFlags & TOUCHEVENTF_UP) {
2029        touch_ids_.erase(input[i].dwID);
2030        touch_event_type = ui::ET_TOUCH_RELEASED;
2031      } else if (input[i].dwFlags & TOUCHEVENTF_MOVE) {
2032        touch_event_type = ui::ET_TOUCH_MOVED;
2033      }
2034      // Handle touch events only on Aura for now.
2035#if defined(USE_AURA)
2036      if (touch_event_type != ui::ET_UNKNOWN) {
2037        POINT point;
2038        point.x = TOUCH_COORD_TO_PIXEL(input[i].x) /
2039            ui::win::GetUndocumentedDPIScale();
2040        point.y = TOUCH_COORD_TO_PIXEL(input[i].y) /
2041            ui::win::GetUndocumentedDPIScale();
2042
2043        ScreenToClient(hwnd(), &point);
2044
2045        ui::TouchEvent event(
2046            touch_event_type,
2047            gfx::Point(point.x, point.y),
2048            input[i].dwID % ui::GestureSequence::kMaxGesturePoints,
2049            base::TimeDelta::FromMilliseconds(input[i].dwTime));
2050        delegate_->HandleTouchEvent(event);
2051      }
2052#endif
2053    }
2054  }
2055  CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(l_param));
2056  SetMsgHandled(FALSE);
2057  return 0;
2058}
2059
2060void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) {
2061  if (ignore_window_pos_changes_) {
2062    // If somebody's trying to toggle our visibility, change the nonclient area,
2063    // change our Z-order, or activate us, we should probably let it go through.
2064    if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) |
2065        SWP_FRAMECHANGED)) &&
2066        (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) {
2067      // Just sizing/moving the window; ignore.
2068      window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW;
2069      window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW);
2070    }
2071  } else if (!GetParent(hwnd())) {
2072    CRect window_rect;
2073    HMONITOR monitor;
2074    gfx::Rect monitor_rect, work_area;
2075    if (GetWindowRect(hwnd(), &window_rect) &&
2076        GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) {
2077      bool work_area_changed = (monitor_rect == last_monitor_rect_) &&
2078                               (work_area != last_work_area_);
2079      if (monitor && (monitor == last_monitor_) &&
2080          ((fullscreen_handler_->fullscreen() &&
2081            !fullscreen_handler_->metro_snap()) ||
2082            work_area_changed)) {
2083        // A rect for the monitor we're on changed.  Normally Windows notifies
2084        // us about this (and thus we're reaching here due to the SetWindowPos()
2085        // call in OnSettingChange() above), but with some software (e.g.
2086        // nVidia's nView desktop manager) the work area can change asynchronous
2087        // to any notification, and we're just sent a SetWindowPos() call with a
2088        // new (frequently incorrect) position/size.  In either case, the best
2089        // response is to throw away the existing position/size information in
2090        // |window_pos| and recalculate it based on the new work rect.
2091        gfx::Rect new_window_rect;
2092        if (fullscreen_handler_->fullscreen()) {
2093          new_window_rect = monitor_rect;
2094        } else if (IsMaximized()) {
2095          new_window_rect = work_area;
2096          int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
2097          new_window_rect.Inset(-border_thickness, -border_thickness);
2098        } else {
2099          new_window_rect = gfx::Rect(window_rect);
2100          new_window_rect.AdjustToFit(work_area);
2101        }
2102        window_pos->x = new_window_rect.x();
2103        window_pos->y = new_window_rect.y();
2104        window_pos->cx = new_window_rect.width();
2105        window_pos->cy = new_window_rect.height();
2106        // WARNING!  Don't set SWP_FRAMECHANGED here, it breaks moving the child
2107        // HWNDs for some reason.
2108        window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW);
2109        window_pos->flags |= SWP_NOCOPYBITS;
2110
2111        // Now ignore all immediately-following SetWindowPos() changes.  Windows
2112        // likes to (incorrectly) recalculate what our position/size should be
2113        // and send us further updates.
2114        ignore_window_pos_changes_ = true;
2115        DCHECK(!ignore_pos_changes_factory_.HasWeakPtrs());
2116        base::MessageLoop::current()->PostTask(
2117            FROM_HERE,
2118            base::Bind(&HWNDMessageHandler::StopIgnoringPosChanges,
2119                       ignore_pos_changes_factory_.GetWeakPtr()));
2120      }
2121      last_monitor_ = monitor;
2122      last_monitor_rect_ = monitor_rect;
2123      last_work_area_ = work_area;
2124    }
2125  }
2126
2127  if (ScopedFullscreenVisibility::IsHiddenForFullscreen(hwnd())) {
2128    // Prevent the window from being made visible if we've been asked to do so.
2129    // See comment in header as to why we might want this.
2130    window_pos->flags &= ~SWP_SHOWWINDOW;
2131  }
2132
2133  SetMsgHandled(FALSE);
2134}
2135
2136void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) {
2137  if (DidClientAreaSizeChange(window_pos))
2138    ClientAreaSizeChanged();
2139  if (remove_standard_frame_ && window_pos->flags & SWP_FRAMECHANGED &&
2140      ui::win::IsAeroGlassEnabled()) {
2141    MARGINS m = {10, 10, 10, 10};
2142    DwmExtendFrameIntoClientArea(hwnd(), &m);
2143  }
2144  if (window_pos->flags & SWP_SHOWWINDOW)
2145    delegate_->HandleVisibilityChanged(true);
2146  else if (window_pos->flags & SWP_HIDEWINDOW)
2147    delegate_->HandleVisibilityChanged(false);
2148  SetMsgHandled(FALSE);
2149}
2150
2151}  // namespace views
2152