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