summaryrefslogtreecommitdiffstats
path: root/views/focus/focus_util_win.cc
blob: 23172ad71bf281456d2041437034e082cbc099d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/focus/focus_util_win.h"

#include <windowsx.h>

#include "app/view_prop.h"
#include "base/auto_reset.h"
#include "base/win_util.h"

using app::ViewProp;

namespace views {

// Property used to indicate the HWND supports having mouse wheel messages
// rerouted to it.
static const char* const kHWNDSupportMouseWheelRerouting =
    "__HWND_MW_REROUTE_OK";

static bool WindowSupportsRerouteMouseWheel(HWND window) {
  while (GetWindowLong(window, GWL_STYLE) & WS_CHILD) {
    if (!IsWindow(window))
      break;

    if (reinterpret_cast<bool>(
            ViewProp::GetValue(window, kHWNDSupportMouseWheelRerouting))) {
      return true;
    }
    window = GetParent(window);
  }
  return false;
}

static bool IsCompatibleWithMouseWheelRedirection(HWND window) {
  std::wstring class_name = win_util::GetClassName(window);
  // Mousewheel redirection to comboboxes is a surprising and
  // undesireable user behavior.
  return !(class_name == L"ComboBox" ||
           class_name == L"ComboBoxEx32");
}

static bool CanRedirectMouseWheelFrom(HWND window) {
  std::wstring class_name = win_util::GetClassName(window);

  // Older Thinkpad mouse wheel drivers create a window under mouse wheel
  // pointer. Detect if we are dealing with this window. In this case we
  // don't need to do anything as the Thinkpad mouse driver will send
  // mouse wheel messages to the right window.
  if ((class_name == L"Syn Visual Class") ||
     (class_name == L"SynTrackCursorWindowClass"))
    return false;

  return true;
}

ViewProp* SetWindowSupportsRerouteMouseWheel(HWND hwnd) {
  return new ViewProp(hwnd, kHWNDSupportMouseWheelRerouting,
                      reinterpret_cast<HANDLE>(true));
}

bool RerouteMouseWheel(HWND window, WPARAM w_param, LPARAM l_param) {
  // Since this is called from a subclass for every window, we can get
  // here recursively. This will happen if, for example, a control
  // reflects wheel scroll messages to its parent. Bail out if we got
  // here recursively.
  static bool recursion_break = false;
  if (recursion_break)
    return false;
  // Check if this window's class has a bad interaction with rerouting.
  if (!IsCompatibleWithMouseWheelRedirection(window))
    return false;

  DWORD current_process = GetCurrentProcessId();
  POINT wheel_location = { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) };
  HWND window_under_wheel = WindowFromPoint(wheel_location);

  if (!CanRedirectMouseWheelFrom(window_under_wheel))
    return false;

  // Find the lowest Chrome window in the hierarchy that can be the
  // target of mouse wheel redirection.
  while (window != window_under_wheel) {
    // If window_under_wheel is not a valid Chrome window, then return true to
    // suppress further processing of the message.
    if (!::IsWindow(window_under_wheel))
      return true;
    DWORD wheel_window_process = 0;
    GetWindowThreadProcessId(window_under_wheel, &wheel_window_process);
    if (current_process != wheel_window_process) {
      if (IsChild(window, window_under_wheel)) {
        // If this message is reflected from a child window in a different
        // process (happens with out of process windowed plugins) then
        // we don't want to reroute the wheel message.
        return false;
      } else {
        // The wheel is scrolling over an unrelated window. Make sure that we
        // have marked that window as supporting mouse wheel rerouting.
        // Otherwise, we cannot send random WM_MOUSEWHEEL messages to arbitrary
        // windows. So just drop the message.
        if (!WindowSupportsRerouteMouseWheel(window_under_wheel))
          return true;
      }
    }

    // window_under_wheel is a Chrome window.  If allowed, redirect.
    if (IsCompatibleWithMouseWheelRedirection(window_under_wheel)) {
      AutoReset<bool> auto_reset_recursion_break(&recursion_break, true);
      SendMessage(window_under_wheel, WM_MOUSEWHEEL, w_param, l_param);
      return true;
    }
    // If redirection is disallowed, try the parent.
    window_under_wheel = GetAncestor(window_under_wheel, GA_PARENT);
  }
  // If we traversed back to the starting point, we should process
  // this message normally; return false.
  return false;
}

}  // namespace views