1 // Copyright (c) 2009 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/base/win/mouse_wheel_util.h" 6 7 #include <windowsx.h> 8 9 #include "base/auto_reset.h" 10 #include "ui/base/view_prop.h" 11 #include "ui/gfx/win/hwnd_util.h" 12 13 namespace ui { 14 15 // Property used to indicate the HWND supports having mouse wheel messages 16 // rerouted to it. 17 static const char* const kHWNDSupportMouseWheelRerouting = 18 "__HWND_MW_REROUTE_OK"; 19 20 static bool WindowSupportsRerouteMouseWheel(HWND window) { 21 while (GetWindowLong(window, GWL_STYLE) & WS_CHILD) { 22 if (!IsWindow(window)) 23 break; 24 25 if (ViewProp::GetValue(window, kHWNDSupportMouseWheelRerouting) != NULL) { 26 return true; 27 } 28 window = GetParent(window); 29 } 30 return false; 31 } 32 33 static bool IsCompatibleWithMouseWheelRedirection(HWND window) { 34 std::wstring class_name = gfx::GetClassName(window); 35 // Mousewheel redirection to comboboxes is a surprising and 36 // undesireable user behavior. 37 return !(class_name == L"ComboBox" || 38 class_name == L"ComboBoxEx32"); 39 } 40 41 static bool CanRedirectMouseWheelFrom(HWND window) { 42 std::wstring class_name = gfx::GetClassName(window); 43 44 // Older Thinkpad mouse wheel drivers create a window under mouse wheel 45 // pointer. Detect if we are dealing with this window. In this case we 46 // don't need to do anything as the Thinkpad mouse driver will send 47 // mouse wheel messages to the right window. 48 if ((class_name == L"Syn Visual Class") || 49 (class_name == L"SynTrackCursorWindowClass")) 50 return false; 51 52 return true; 53 } 54 55 ViewProp* SetWindowSupportsRerouteMouseWheel(HWND hwnd) { 56 return new ViewProp(hwnd, kHWNDSupportMouseWheelRerouting, 57 reinterpret_cast<HANDLE>(true)); 58 } 59 60 bool RerouteMouseWheel(HWND window, WPARAM w_param, LPARAM l_param) { 61 // Since this is called from a subclass for every window, we can get 62 // here recursively. This will happen if, for example, a control 63 // reflects wheel scroll messages to its parent. Bail out if we got 64 // here recursively. 65 static bool recursion_break = false; 66 if (recursion_break) 67 return false; 68 // Check if this window's class has a bad interaction with rerouting. 69 if (!IsCompatibleWithMouseWheelRedirection(window)) 70 return false; 71 72 DWORD current_process = GetCurrentProcessId(); 73 POINT wheel_location = { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) }; 74 HWND window_under_wheel = WindowFromPoint(wheel_location); 75 76 if (!CanRedirectMouseWheelFrom(window_under_wheel)) 77 return false; 78 79 // Find the lowest Chrome window in the hierarchy that can be the 80 // target of mouse wheel redirection. 81 while (window != window_under_wheel) { 82 // If window_under_wheel is not a valid Chrome window, then return true to 83 // suppress further processing of the message. 84 if (!::IsWindow(window_under_wheel)) 85 return true; 86 DWORD wheel_window_process = 0; 87 GetWindowThreadProcessId(window_under_wheel, &wheel_window_process); 88 if (current_process != wheel_window_process) { 89 if (IsChild(window, window_under_wheel)) { 90 // If this message is reflected from a child window in a different 91 // process (happens with out of process windowed plugins) then 92 // we don't want to reroute the wheel message. 93 return false; 94 } else { 95 // The wheel is scrolling over an unrelated window. Make sure that we 96 // have marked that window as supporting mouse wheel rerouting. 97 // Otherwise, we cannot send random WM_MOUSEWHEEL messages to arbitrary 98 // windows. So just drop the message. 99 if (!WindowSupportsRerouteMouseWheel(window_under_wheel)) 100 return true; 101 } 102 } 103 104 // window_under_wheel is a Chrome window. If allowed, redirect. 105 if (IsCompatibleWithMouseWheelRedirection(window_under_wheel)) { 106 base::AutoReset<bool> auto_reset_recursion_break(&recursion_break, true); 107 SendMessage(window_under_wheel, WM_MOUSEWHEEL, w_param, l_param); 108 return true; 109 } 110 // If redirection is disallowed, try the parent. 111 window_under_wheel = GetAncestor(window_under_wheel, GA_PARENT); 112 } 113 // If we traversed back to the starting point, we should process 114 // this message normally; return false. 115 return false; 116 } 117 118 } // namespace ui 119