| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/base/test/ui_controls_internal_win.h" | 5 #include "ui/base/test/ui_controls_internal_win.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback.h" | 8 #include "base/callback.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/macros.h" | 11 #include "base/macros.h" |
| 12 #include "base/memory/ref_counted.h" | 12 #include "base/memory/ref_counted.h" |
| 13 #include "base/single_thread_task_runner.h" | 13 #include "base/single_thread_task_runner.h" |
| 14 #include "base/threading/thread_task_runner_handle.h" | 14 #include "base/threading/thread_task_runner_handle.h" |
| 15 #include "ui/events/keycodes/keyboard_code_conversion_win.h" | 15 #include "ui/events/keycodes/keyboard_code_conversion_win.h" |
| 16 #include "ui/events/keycodes/keyboard_codes.h" | 16 #include "ui/events/keycodes/keyboard_codes.h" |
| 17 | 17 |
| 18 namespace ui_controls { |
| 19 |
| 20 // TODO(msw): Remove flaky test debug logging for http://crbug.com/639350. |
| 21 bool g_crbug_639350_logging = false; |
| 22 |
| 23 namespace internal { |
| 24 |
| 18 namespace { | 25 namespace { |
| 19 | 26 |
| 20 // InputDispatcher ------------------------------------------------------------ | 27 // InputDispatcher ------------------------------------------------------------ |
| 21 | 28 |
| 22 // InputDispatcher is used to listen for a mouse/keyboard event. When the | 29 // InputDispatcher is used to listen for a mouse/keyboard event. When the |
| 23 // appropriate event is received the task is notified. | 30 // appropriate event is received the task is notified. |
| 24 class InputDispatcher : public base::RefCounted<InputDispatcher> { | 31 class InputDispatcher : public base::RefCounted<InputDispatcher> { |
| 25 public: | 32 public: |
| 26 InputDispatcher(const base::Closure& task, WPARAM message_waiting_for); | 33 InputDispatcher(const base::Closure& task, WPARAM message_waiting_for); |
| 27 | 34 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 54 bool installed_hook_ = false; | 61 bool installed_hook_ = false; |
| 55 | 62 |
| 56 // Return value from SetWindowsHookEx. | 63 // Return value from SetWindowsHookEx. |
| 57 HHOOK next_hook_ = NULL; | 64 HHOOK next_hook_ = NULL; |
| 58 | 65 |
| 59 // If a hook is installed, this is the dispatcher. | 66 // If a hook is installed, this is the dispatcher. |
| 60 InputDispatcher* current_dispatcher_ = NULL; | 67 InputDispatcher* current_dispatcher_ = NULL; |
| 61 | 68 |
| 62 // Callback from hook when a mouse message is received. | 69 // Callback from hook when a mouse message is received. |
| 63 LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param) { | 70 LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param) { |
| 71 if (g_crbug_639350_logging) |
| 72 LOG(ERROR) << "crbug.com/639350 MouseHook A"; |
| 64 HHOOK next_hook = next_hook_; | 73 HHOOK next_hook = next_hook_; |
| 65 if (n_code == HC_ACTION) { | 74 if (n_code == HC_ACTION) { |
| 66 DCHECK(current_dispatcher_); | 75 DCHECK(current_dispatcher_); |
| 67 current_dispatcher_->DispatchedMessage(w_param); | 76 current_dispatcher_->DispatchedMessage(w_param); |
| 68 } | 77 } |
| 69 return CallNextHookEx(next_hook, n_code, w_param, l_param); | 78 return CallNextHookEx(next_hook, n_code, w_param, l_param); |
| 70 } | 79 } |
| 71 | 80 |
| 72 // Callback from hook when a key message is received. | 81 // Callback from hook when a key message is received. |
| 73 LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param) { | 82 LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param) { |
| 83 if (g_crbug_639350_logging) |
| 84 LOG(ERROR) << "crbug.com/639350 KeyHook A"; |
| 74 HHOOK next_hook = next_hook_; | 85 HHOOK next_hook = next_hook_; |
| 75 if (n_code == HC_ACTION) { | 86 if (n_code == HC_ACTION) { |
| 87 if (g_crbug_639350_logging) |
| 88 LOG(ERROR) << "crbug.com/639350 KeyHook B"; |
| 76 DCHECK(current_dispatcher_); | 89 DCHECK(current_dispatcher_); |
| 77 if (l_param & (1 << 30)) { | 90 if (l_param & (1 << 30)) { |
| 91 if (g_crbug_639350_logging) |
| 92 LOG(ERROR) << "crbug.com/639350 KeyHook C"; |
| 78 // Only send on key up. | 93 // Only send on key up. |
| 79 current_dispatcher_->MatchingMessageFound(); | 94 current_dispatcher_->MatchingMessageFound(); |
| 80 } | 95 } |
| 81 } | 96 } |
| 82 return CallNextHookEx(next_hook, n_code, w_param, l_param); | 97 if (g_crbug_639350_logging) |
| 98 LOG(ERROR) << "crbug.com/639350 KeyHook D"; |
| 99 LRESULT result = CallNextHookEx(next_hook, n_code, w_param, l_param); |
| 100 if (g_crbug_639350_logging) |
| 101 LOG(ERROR) << "crbug.com/639350 KeyHook E"; |
| 102 return result; |
| 83 } | 103 } |
| 84 | 104 |
| 85 // Installs dispatcher as the current hook. | 105 // Installs dispatcher as the current hook. |
| 86 void InstallHook(InputDispatcher* dispatcher, bool key_hook) { | 106 void InstallHook(InputDispatcher* dispatcher, bool key_hook) { |
| 107 if (g_crbug_639350_logging) |
| 108 LOG(ERROR) << "crbug.com/639350 InstallHook A"; |
| 87 DCHECK(!installed_hook_); | 109 DCHECK(!installed_hook_); |
| 88 current_dispatcher_ = dispatcher; | 110 current_dispatcher_ = dispatcher; |
| 89 installed_hook_ = true; | 111 installed_hook_ = true; |
| 90 if (key_hook) { | 112 if (key_hook) { |
| 113 if (g_crbug_639350_logging) |
| 114 LOG(ERROR) << "crbug.com/639350 InstallHook B"; |
| 91 next_hook_ = SetWindowsHookEx(WH_KEYBOARD, &KeyHook, NULL, | 115 next_hook_ = SetWindowsHookEx(WH_KEYBOARD, &KeyHook, NULL, |
| 92 GetCurrentThreadId()); | 116 GetCurrentThreadId()); |
| 93 } else { | 117 } else { |
| 118 if (g_crbug_639350_logging) |
| 119 LOG(ERROR) << "crbug.com/639350 InstallHook C"; |
| 94 // NOTE: I originally tried WH_CALLWNDPROCRET, but for some reason I | 120 // NOTE: I originally tried WH_CALLWNDPROCRET, but for some reason I |
| 95 // didn't get a mouse message like I do with MouseHook. | 121 // didn't get a mouse message like I do with MouseHook. |
| 96 next_hook_ = SetWindowsHookEx(WH_MOUSE, &MouseHook, NULL, | 122 next_hook_ = SetWindowsHookEx(WH_MOUSE, &MouseHook, NULL, |
| 97 GetCurrentThreadId()); | 123 GetCurrentThreadId()); |
| 98 } | 124 } |
| 125 if (g_crbug_639350_logging) |
| 126 LOG(ERROR) << "crbug.com/639350 InstallHook D"; |
| 99 DCHECK(next_hook_); | 127 DCHECK(next_hook_); |
| 100 } | 128 } |
| 101 | 129 |
| 102 // Uninstalls the hook set in InstallHook. | 130 // Uninstalls the hook set in InstallHook. |
| 103 void UninstallHook(InputDispatcher* dispatcher) { | 131 void UninstallHook(InputDispatcher* dispatcher) { |
| 132 if (g_crbug_639350_logging) |
| 133 LOG(ERROR) << "crbug.com/639350 UninstallHook A"; |
| 104 if (current_dispatcher_ == dispatcher) { | 134 if (current_dispatcher_ == dispatcher) { |
| 135 if (g_crbug_639350_logging) |
| 136 LOG(ERROR) << "crbug.com/639350 UninstallHook B"; |
| 105 installed_hook_ = false; | 137 installed_hook_ = false; |
| 106 current_dispatcher_ = NULL; | 138 current_dispatcher_ = NULL; |
| 107 UnhookWindowsHookEx(next_hook_); | 139 UnhookWindowsHookEx(next_hook_); |
| 108 } | 140 } |
| 141 if (g_crbug_639350_logging) |
| 142 LOG(ERROR) << "crbug.com/639350 UninstallHook C"; |
| 109 } | 143 } |
| 110 | 144 |
| 111 InputDispatcher::InputDispatcher(const base::Closure& task, | 145 InputDispatcher::InputDispatcher(const base::Closure& task, |
| 112 WPARAM message_waiting_for) | 146 WPARAM message_waiting_for) |
| 113 : task_(task), message_waiting_for_(message_waiting_for) { | 147 : task_(task), message_waiting_for_(message_waiting_for) { |
| 148 if (g_crbug_639350_logging) |
| 149 LOG(ERROR) << "crbug.com/639350 InputDispatcher::InputDispatcher A"; |
| 114 InstallHook(this, message_waiting_for == WM_KEYUP); | 150 InstallHook(this, message_waiting_for == WM_KEYUP); |
| 151 if (g_crbug_639350_logging) |
| 152 LOG(ERROR) << "crbug.com/639350 InputDispatcher::InputDispatcher B"; |
| 115 } | 153 } |
| 116 | 154 |
| 117 InputDispatcher::~InputDispatcher() { | 155 InputDispatcher::~InputDispatcher() { |
| 156 if (g_crbug_639350_logging) |
| 157 LOG(ERROR) << "crbug.com/639350 InputDispatcher::~InputDispatcher A"; |
| 118 // Make sure the hook isn't installed. | 158 // Make sure the hook isn't installed. |
| 119 UninstallHook(this); | 159 UninstallHook(this); |
| 160 if (g_crbug_639350_logging) |
| 161 LOG(ERROR) << "crbug.com/639350 InputDispatcher::~InputDispatcher B"; |
| 120 } | 162 } |
| 121 | 163 |
| 122 void InputDispatcher::DispatchedMessage(WPARAM message) { | 164 void InputDispatcher::DispatchedMessage(WPARAM message) { |
| 123 if (message == message_waiting_for_) | 165 if (g_crbug_639350_logging) |
| 166 LOG(ERROR) << "crbug.com/639350 InputDispatcher::DispatchedMessage A"; |
| 167 if (message == message_waiting_for_) { |
| 168 if (g_crbug_639350_logging) |
| 169 LOG(ERROR) << "crbug.com/639350 InputDispatcher::DispatchedMessage B"; |
| 124 MatchingMessageFound(); | 170 MatchingMessageFound(); |
| 171 } |
| 172 if (g_crbug_639350_logging) |
| 173 LOG(ERROR) << "crbug.com/639350 InputDispatcher::DispatchedMessage C"; |
| 125 } | 174 } |
| 126 | 175 |
| 127 void InputDispatcher::MatchingMessageFound() { | 176 void InputDispatcher::MatchingMessageFound() { |
| 177 if (g_crbug_639350_logging) |
| 178 LOG(ERROR) << "crbug.com/639350 InputDispatcher::MatchingMessageFound A"; |
| 128 UninstallHook(this); | 179 UninstallHook(this); |
| 180 if (g_crbug_639350_logging) |
| 181 LOG(ERROR) << "crbug.com/639350 InputDispatcher::MatchingMessageFound B"; |
| 129 // At the time we're invoked the event has not actually been processed. | 182 // At the time we're invoked the event has not actually been processed. |
| 130 // Use PostTask to make sure the event has been processed before notifying. | 183 // Use PostTask to make sure the event has been processed before notifying. |
| 131 base::ThreadTaskRunnerHandle::Get()->PostTask( | 184 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 132 FROM_HERE, base::Bind(&InputDispatcher::NotifyTask, this)); | 185 FROM_HERE, base::Bind(&InputDispatcher::NotifyTask, this)); |
| 186 if (g_crbug_639350_logging) |
| 187 LOG(ERROR) << "crbug.com/639350 InputDispatcher::MatchingMessageFound C"; |
| 133 } | 188 } |
| 134 | 189 |
| 135 void InputDispatcher::NotifyTask() { | 190 void InputDispatcher::NotifyTask() { |
| 191 if (g_crbug_639350_logging) |
| 192 LOG(ERROR) << "crbug.com/639350 InputDispatcher::NotifyTask A"; |
| 136 task_.Run(); | 193 task_.Run(); |
| 194 if (g_crbug_639350_logging) |
| 195 LOG(ERROR) << "crbug.com/639350 InputDispatcher::NotifyTask B"; |
| 137 Release(); | 196 Release(); |
| 197 if (g_crbug_639350_logging) |
| 198 LOG(ERROR) << "crbug.com/639350 InputDispatcher::NotifyTask C"; |
| 138 } | 199 } |
| 139 | 200 |
| 140 // Private functions ---------------------------------------------------------- | 201 // Private functions ---------------------------------------------------------- |
| 141 | 202 |
| 142 // Populate the INPUT structure with the appropriate keyboard event | 203 // Populate the INPUT structure with the appropriate keyboard event |
| 143 // parameters required by SendInput | 204 // parameters required by SendInput |
| 144 bool FillKeyboardInput(ui::KeyboardCode key, INPUT* input, bool key_up) { | 205 bool FillKeyboardInput(ui::KeyboardCode key, INPUT* input, bool key_up) { |
| 206 if (g_crbug_639350_logging) |
| 207 LOG(ERROR) << "crbug.com/639350 FillKeyboardInput A"; |
| 145 memset(input, 0, sizeof(INPUT)); | 208 memset(input, 0, sizeof(INPUT)); |
| 146 input->type = INPUT_KEYBOARD; | 209 input->type = INPUT_KEYBOARD; |
| 147 input->ki.wVk = ui::WindowsKeyCodeForKeyboardCode(key); | 210 input->ki.wVk = ui::WindowsKeyCodeForKeyboardCode(key); |
| 148 input->ki.dwFlags = key_up ? KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP : | 211 input->ki.dwFlags = key_up ? KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP : |
| 149 KEYEVENTF_EXTENDEDKEY; | 212 KEYEVENTF_EXTENDEDKEY; |
| 213 if (g_crbug_639350_logging) |
| 214 LOG(ERROR) << "crbug.com/639350 FillKeyboardInput B"; |
| 150 | 215 |
| 151 return true; | 216 return true; |
| 152 } | 217 } |
| 153 | 218 |
| 154 } // namespace | 219 } // namespace |
| 155 | 220 |
| 156 namespace ui_controls { | |
| 157 namespace internal { | |
| 158 | |
| 159 bool SendKeyPressImpl(HWND window, | 221 bool SendKeyPressImpl(HWND window, |
| 160 ui::KeyboardCode key, | 222 ui::KeyboardCode key, |
| 161 bool control, | 223 bool control, |
| 162 bool shift, | 224 bool shift, |
| 163 bool alt, | 225 bool alt, |
| 164 const base::Closure& task) { | 226 const base::Closure& task) { |
| 227 if (g_crbug_639350_logging) |
| 228 LOG(ERROR) << "crbug.com/639350 SendKeyPressImpl A"; |
| 165 // SendInput only works as we expect it if one of our windows is the | 229 // SendInput only works as we expect it if one of our windows is the |
| 166 // foreground window already. | 230 // foreground window already. |
| 167 HWND target_window = (::GetActiveWindow() && | 231 HWND target_window = (::GetActiveWindow() && |
| 168 ::GetWindow(::GetActiveWindow(), GW_OWNER) == window) ? | 232 ::GetWindow(::GetActiveWindow(), GW_OWNER) == window) ? |
| 169 ::GetActiveWindow() : | 233 ::GetActiveWindow() : |
| 170 window; | 234 window; |
| 235 if (g_crbug_639350_logging) |
| 236 LOG(ERROR) << "crbug.com/639350 SendKeyPressImpl B"; |
| 171 if (window && ::GetForegroundWindow() != target_window) | 237 if (window && ::GetForegroundWindow() != target_window) |
| 172 return false; | 238 return false; |
| 239 if (g_crbug_639350_logging) |
| 240 LOG(ERROR) << "crbug.com/639350 SendKeyPressImpl C"; |
| 173 | 241 |
| 174 scoped_refptr<InputDispatcher> dispatcher( | 242 scoped_refptr<InputDispatcher> dispatcher( |
| 175 !task.is_null() ? new InputDispatcher(task, WM_KEYUP) : NULL); | 243 !task.is_null() ? new InputDispatcher(task, WM_KEYUP) : NULL); |
| 176 | 244 |
| 245 if (g_crbug_639350_logging) |
| 246 LOG(ERROR) << "crbug.com/639350 SendKeyPressImpl D"; |
| 247 |
| 177 // If a pop-up menu is open, it won't receive events sent using SendInput. | 248 // If a pop-up menu is open, it won't receive events sent using SendInput. |
| 178 // Check for a pop-up menu using its window class (#32768) and if one | 249 // Check for a pop-up menu using its window class (#32768) and if one |
| 179 // exists, send the key event directly there. | 250 // exists, send the key event directly there. |
| 180 HWND popup_menu = ::FindWindow(L"#32768", 0); | 251 HWND popup_menu = ::FindWindow(L"#32768", 0); |
| 181 if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) { | 252 if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) { |
| 182 WPARAM w_param = ui::WindowsKeyCodeForKeyboardCode(key); | 253 WPARAM w_param = ui::WindowsKeyCodeForKeyboardCode(key); |
| 183 LPARAM l_param = 0; | 254 LPARAM l_param = 0; |
| 184 ::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param); | 255 ::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param); |
| 185 ::SendMessage(popup_menu, WM_KEYUP, w_param, l_param); | 256 ::SendMessage(popup_menu, WM_KEYUP, w_param, l_param); |
| 186 | 257 |
| 187 if (dispatcher.get()) | 258 if (dispatcher.get()) |
| 188 dispatcher->AddRef(); | 259 dispatcher->AddRef(); |
| 189 return true; | 260 return true; |
| 190 } | 261 } |
| 262 if (g_crbug_639350_logging) |
| 263 LOG(ERROR) << "crbug.com/639350 SendKeyPressImpl E"; |
| 191 | 264 |
| 192 INPUT input[8] = {}; // 8, assuming all the modifiers are activated. | 265 INPUT input[8] = {}; // 8, assuming all the modifiers are activated. |
| 193 | 266 |
| 194 UINT i = 0; | 267 UINT i = 0; |
| 195 if (control) { | 268 if (control) { |
| 196 if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], false)) | 269 if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], false)) |
| 197 return false; | 270 return false; |
| 198 i++; | 271 i++; |
| 199 } | 272 } |
| 200 | 273 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 228 if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], true)) | 301 if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], true)) |
| 229 return false; | 302 return false; |
| 230 i++; | 303 i++; |
| 231 } | 304 } |
| 232 | 305 |
| 233 if (control) { | 306 if (control) { |
| 234 if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], true)) | 307 if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], true)) |
| 235 return false; | 308 return false; |
| 236 i++; | 309 i++; |
| 237 } | 310 } |
| 311 if (g_crbug_639350_logging) |
| 312 LOG(ERROR) << "crbug.com/639350 SendKeyPressImpl F"; |
| 238 | 313 |
| 239 if (::SendInput(i, input, sizeof(INPUT)) != i) | 314 if (::SendInput(i, input, sizeof(INPUT)) != i) |
| 240 return false; | 315 return false; |
| 241 | 316 |
| 317 if (g_crbug_639350_logging) |
| 318 LOG(ERROR) << "crbug.com/639350 SendKeyPressImpl G"; |
| 242 if (dispatcher.get()) | 319 if (dispatcher.get()) |
| 243 dispatcher->AddRef(); | 320 dispatcher->AddRef(); |
| 244 | 321 |
| 322 if (g_crbug_639350_logging) |
| 323 LOG(ERROR) << "crbug.com/639350 SendKeyPressImpl H"; |
| 245 return true; | 324 return true; |
| 246 } | 325 } |
| 247 | 326 |
| 248 bool SendMouseMoveImpl(long screen_x, | 327 bool SendMouseMoveImpl(long screen_x, |
| 249 long screen_y, | 328 long screen_y, |
| 250 const base::Closure& task) { | 329 const base::Closure& task) { |
| 251 // First check if the mouse is already there. | 330 // First check if the mouse is already there. |
| 252 POINT current_pos; | 331 POINT current_pos; |
| 253 ::GetCursorPos(¤t_pos); | 332 ::GetCursorPos(¤t_pos); |
| 254 if (screen_x == current_pos.x && screen_y == current_pos.y) { | 333 if (screen_x == current_pos.x && screen_y == current_pos.y) { |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 325 return false; | 404 return false; |
| 326 | 405 |
| 327 if (dispatcher.get()) | 406 if (dispatcher.get()) |
| 328 dispatcher->AddRef(); | 407 dispatcher->AddRef(); |
| 329 | 408 |
| 330 return true; | 409 return true; |
| 331 } | 410 } |
| 332 | 411 |
| 333 } // namespace internal | 412 } // namespace internal |
| 334 } // namespace ui_controls | 413 } // namespace ui_controls |
| OLD | NEW |