| OLD | NEW |
| (Empty) |
| 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 "chrome/browser/automation/ui_controls_internal.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/memory/ref_counted.h" | |
| 11 #include "base/message_loop.h" | |
| 12 #include "ui/base/keycodes/keyboard_code_conversion_win.h" | |
| 13 #include "ui/base/keycodes/keyboard_codes.h" | |
| 14 | |
| 15 #if defined(USE_AURA) | |
| 16 #include "ui/aura/root_window.h" | |
| 17 #include "ui/aura/window.h" | |
| 18 #endif | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // InputDispatcher ------------------------------------------------------------ | |
| 23 | |
| 24 // InputDispatcher is used to listen for a mouse/keyboard event. When the | |
| 25 // appropriate event is received the task is notified. | |
| 26 class InputDispatcher : public base::RefCounted<InputDispatcher> { | |
| 27 public: | |
| 28 InputDispatcher(const base::Closure& task, WPARAM message_waiting_for); | |
| 29 | |
| 30 // Invoked from the hook. If mouse_message matches message_waiting_for_ | |
| 31 // MatchingMessageFound is invoked. | |
| 32 void DispatchedMessage(WPARAM mouse_message); | |
| 33 | |
| 34 // Invoked when a matching event is found. Uninstalls the hook and schedules | |
| 35 // an event that notifies the task. | |
| 36 void MatchingMessageFound(); | |
| 37 | |
| 38 private: | |
| 39 friend class base::RefCounted<InputDispatcher>; | |
| 40 | |
| 41 ~InputDispatcher(); | |
| 42 | |
| 43 // Notifies the task and release this (which should delete it). | |
| 44 void NotifyTask(); | |
| 45 | |
| 46 // The task we notify. | |
| 47 base::Closure task_; | |
| 48 | |
| 49 // Message we're waiting for. Not used for keyboard events. | |
| 50 const WPARAM message_waiting_for_; | |
| 51 | |
| 52 DISALLOW_COPY_AND_ASSIGN(InputDispatcher); | |
| 53 }; | |
| 54 | |
| 55 // Have we installed the hook? | |
| 56 bool installed_hook_ = false; | |
| 57 | |
| 58 // Return value from SetWindowsHookEx. | |
| 59 HHOOK next_hook_ = NULL; | |
| 60 | |
| 61 // If a hook is installed, this is the dispatcher. | |
| 62 InputDispatcher* current_dispatcher_ = NULL; | |
| 63 | |
| 64 // Callback from hook when a mouse message is received. | |
| 65 LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param) { | |
| 66 HHOOK next_hook = next_hook_; | |
| 67 if (n_code == HC_ACTION) { | |
| 68 DCHECK(current_dispatcher_); | |
| 69 current_dispatcher_->DispatchedMessage(w_param); | |
| 70 } | |
| 71 return CallNextHookEx(next_hook, n_code, w_param, l_param); | |
| 72 } | |
| 73 | |
| 74 // Callback from hook when a key message is received. | |
| 75 LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param) { | |
| 76 HHOOK next_hook = next_hook_; | |
| 77 if (n_code == HC_ACTION) { | |
| 78 DCHECK(current_dispatcher_); | |
| 79 if (l_param & (1 << 30)) { | |
| 80 // Only send on key up. | |
| 81 current_dispatcher_->MatchingMessageFound(); | |
| 82 } | |
| 83 } | |
| 84 return CallNextHookEx(next_hook, n_code, w_param, l_param); | |
| 85 } | |
| 86 | |
| 87 // Installs dispatcher as the current hook. | |
| 88 void InstallHook(InputDispatcher* dispatcher, bool key_hook) { | |
| 89 DCHECK(!installed_hook_); | |
| 90 current_dispatcher_ = dispatcher; | |
| 91 installed_hook_ = true; | |
| 92 if (key_hook) { | |
| 93 next_hook_ = SetWindowsHookEx(WH_KEYBOARD, &KeyHook, NULL, | |
| 94 GetCurrentThreadId()); | |
| 95 } else { | |
| 96 // NOTE: I originally tried WH_CALLWNDPROCRET, but for some reason I | |
| 97 // didn't get a mouse message like I do with MouseHook. | |
| 98 next_hook_ = SetWindowsHookEx(WH_MOUSE, &MouseHook, NULL, | |
| 99 GetCurrentThreadId()); | |
| 100 } | |
| 101 DCHECK(next_hook_); | |
| 102 } | |
| 103 | |
| 104 // Uninstalls the hook set in InstallHook. | |
| 105 void UninstallHook(InputDispatcher* dispatcher) { | |
| 106 if (current_dispatcher_ == dispatcher) { | |
| 107 installed_hook_ = false; | |
| 108 current_dispatcher_ = NULL; | |
| 109 UnhookWindowsHookEx(next_hook_); | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 InputDispatcher::InputDispatcher(const base::Closure& task, | |
| 114 UINT message_waiting_for) | |
| 115 : task_(task), message_waiting_for_(message_waiting_for) { | |
| 116 InstallHook(this, message_waiting_for == WM_KEYUP); | |
| 117 } | |
| 118 | |
| 119 InputDispatcher::~InputDispatcher() { | |
| 120 // Make sure the hook isn't installed. | |
| 121 UninstallHook(this); | |
| 122 } | |
| 123 | |
| 124 void InputDispatcher::DispatchedMessage(WPARAM message) { | |
| 125 if (message == message_waiting_for_) | |
| 126 MatchingMessageFound(); | |
| 127 } | |
| 128 | |
| 129 void InputDispatcher::MatchingMessageFound() { | |
| 130 UninstallHook(this); | |
| 131 // At the time we're invoked the event has not actually been processed. | |
| 132 // Use PostTask to make sure the event has been processed before notifying. | |
| 133 MessageLoop::current()->PostTask( | |
| 134 FROM_HERE, base::Bind(&InputDispatcher::NotifyTask, this)); | |
| 135 } | |
| 136 | |
| 137 void InputDispatcher::NotifyTask() { | |
| 138 task_.Run(); | |
| 139 Release(); | |
| 140 } | |
| 141 | |
| 142 // Private functions ---------------------------------------------------------- | |
| 143 | |
| 144 // Populate the INPUT structure with the appropriate keyboard event | |
| 145 // parameters required by SendInput | |
| 146 bool FillKeyboardInput(ui::KeyboardCode key, INPUT* input, bool key_up) { | |
| 147 memset(input, 0, sizeof(INPUT)); | |
| 148 input->type = INPUT_KEYBOARD; | |
| 149 input->ki.wVk = ui::WindowsKeyCodeForKeyboardCode(key); | |
| 150 input->ki.dwFlags = key_up ? KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP : | |
| 151 KEYEVENTF_EXTENDEDKEY; | |
| 152 | |
| 153 return true; | |
| 154 } | |
| 155 | |
| 156 // Send a key event (up/down) | |
| 157 bool SendKeyEvent(ui::KeyboardCode key, bool up) { | |
| 158 INPUT input = { 0 }; | |
| 159 | |
| 160 if (!FillKeyboardInput(key, &input, up)) | |
| 161 return false; | |
| 162 | |
| 163 if (!::SendInput(1, &input, sizeof(INPUT))) | |
| 164 return false; | |
| 165 | |
| 166 return true; | |
| 167 } | |
| 168 | |
| 169 } // namespace | |
| 170 | |
| 171 namespace ui_controls { | |
| 172 namespace internal { | |
| 173 | |
| 174 bool SendKeyPressImpl(gfx::NativeWindow native_window, | |
| 175 ui::KeyboardCode key, | |
| 176 bool control, | |
| 177 bool shift, | |
| 178 bool alt, | |
| 179 const base::Closure& task) { | |
| 180 // SendInput only works as we expect it if one of our windows is the | |
| 181 // foreground window already. | |
| 182 #if defined(USE_AURA) | |
| 183 HWND window = native_window->GetRootWindow()->GetAcceleratedWidget(); | |
| 184 #else | |
| 185 HWND window = native_window; | |
| 186 #endif | |
| 187 HWND target_window = (::GetActiveWindow() && | |
| 188 ::GetWindow(::GetActiveWindow(), GW_OWNER) == window) ? | |
| 189 ::GetActiveWindow() : | |
| 190 window; | |
| 191 if (window && ::GetForegroundWindow() != target_window) | |
| 192 return false; | |
| 193 | |
| 194 scoped_refptr<InputDispatcher> dispatcher( | |
| 195 !task.is_null() ? new InputDispatcher(task, WM_KEYUP) : NULL); | |
| 196 | |
| 197 // If a pop-up menu is open, it won't receive events sent using SendInput. | |
| 198 // Check for a pop-up menu using its window class (#32768) and if one | |
| 199 // exists, send the key event directly there. | |
| 200 HWND popup_menu = ::FindWindow(L"#32768", 0); | |
| 201 if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) { | |
| 202 WPARAM w_param = ui::WindowsKeyCodeForKeyboardCode(key); | |
| 203 LPARAM l_param = 0; | |
| 204 ::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param); | |
| 205 ::SendMessage(popup_menu, WM_KEYUP, w_param, l_param); | |
| 206 | |
| 207 if (dispatcher.get()) | |
| 208 dispatcher->AddRef(); | |
| 209 return true; | |
| 210 } | |
| 211 | |
| 212 INPUT input[8] = { 0 }; // 8, assuming all the modifiers are activated. | |
| 213 | |
| 214 UINT i = 0; | |
| 215 if (control) { | |
| 216 if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], false)) | |
| 217 return false; | |
| 218 i++; | |
| 219 } | |
| 220 | |
| 221 if (shift) { | |
| 222 if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], false)) | |
| 223 return false; | |
| 224 i++; | |
| 225 } | |
| 226 | |
| 227 if (alt) { | |
| 228 if (!FillKeyboardInput(ui::VKEY_MENU, &input[i], false)) | |
| 229 return false; | |
| 230 i++; | |
| 231 } | |
| 232 | |
| 233 if (!FillKeyboardInput(key, &input[i], false)) | |
| 234 return false; | |
| 235 i++; | |
| 236 | |
| 237 if (!FillKeyboardInput(key, &input[i], true)) | |
| 238 return false; | |
| 239 i++; | |
| 240 | |
| 241 if (alt) { | |
| 242 if (!FillKeyboardInput(ui::VKEY_MENU, &input[i], true)) | |
| 243 return false; | |
| 244 i++; | |
| 245 } | |
| 246 | |
| 247 if (shift) { | |
| 248 if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], true)) | |
| 249 return false; | |
| 250 i++; | |
| 251 } | |
| 252 | |
| 253 if (control) { | |
| 254 if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], true)) | |
| 255 return false; | |
| 256 i++; | |
| 257 } | |
| 258 | |
| 259 if (::SendInput(i, input, sizeof(INPUT)) != i) | |
| 260 return false; | |
| 261 | |
| 262 if (dispatcher.get()) | |
| 263 dispatcher->AddRef(); | |
| 264 | |
| 265 return true; | |
| 266 } | |
| 267 | |
| 268 bool SendMouseMoveImpl(long x, long y, const base::Closure& task) { | |
| 269 // First check if the mouse is already there. | |
| 270 POINT current_pos; | |
| 271 ::GetCursorPos(¤t_pos); | |
| 272 if (x == current_pos.x && y == current_pos.y) { | |
| 273 if (!task.is_null()) | |
| 274 MessageLoop::current()->PostTask(FROM_HERE, task); | |
| 275 return true; | |
| 276 } | |
| 277 | |
| 278 INPUT input = { 0 }; | |
| 279 | |
| 280 int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1; | |
| 281 int screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1; | |
| 282 LONG pixel_x = static_cast<LONG>(x * (65535.0f / screen_width)); | |
| 283 LONG pixel_y = static_cast<LONG>(y * (65535.0f / screen_height)); | |
| 284 | |
| 285 input.type = INPUT_MOUSE; | |
| 286 input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; | |
| 287 input.mi.dx = pixel_x; | |
| 288 input.mi.dy = pixel_y; | |
| 289 | |
| 290 scoped_refptr<InputDispatcher> dispatcher( | |
| 291 !task.is_null() ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL); | |
| 292 | |
| 293 if (!::SendInput(1, &input, sizeof(INPUT))) | |
| 294 return false; | |
| 295 | |
| 296 if (dispatcher.get()) | |
| 297 dispatcher->AddRef(); | |
| 298 | |
| 299 return true; | |
| 300 } | |
| 301 | |
| 302 bool SendMouseEventsImpl(MouseButton type, int state, | |
| 303 const base::Closure& task) { | |
| 304 DWORD down_flags = MOUSEEVENTF_ABSOLUTE; | |
| 305 DWORD up_flags = MOUSEEVENTF_ABSOLUTE; | |
| 306 UINT last_event; | |
| 307 | |
| 308 switch (type) { | |
| 309 case LEFT: | |
| 310 down_flags |= MOUSEEVENTF_LEFTDOWN; | |
| 311 up_flags |= MOUSEEVENTF_LEFTUP; | |
| 312 last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN; | |
| 313 break; | |
| 314 | |
| 315 case MIDDLE: | |
| 316 down_flags |= MOUSEEVENTF_MIDDLEDOWN; | |
| 317 up_flags |= MOUSEEVENTF_MIDDLEUP; | |
| 318 last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN; | |
| 319 break; | |
| 320 | |
| 321 case RIGHT: | |
| 322 down_flags |= MOUSEEVENTF_RIGHTDOWN; | |
| 323 up_flags |= MOUSEEVENTF_RIGHTUP; | |
| 324 last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN; | |
| 325 break; | |
| 326 | |
| 327 default: | |
| 328 NOTREACHED(); | |
| 329 return false; | |
| 330 } | |
| 331 | |
| 332 scoped_refptr<InputDispatcher> dispatcher( | |
| 333 !task.is_null() ? new InputDispatcher(task, last_event) : NULL); | |
| 334 | |
| 335 INPUT input = { 0 }; | |
| 336 input.type = INPUT_MOUSE; | |
| 337 input.mi.dwFlags = down_flags; | |
| 338 if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT))) | |
| 339 return false; | |
| 340 | |
| 341 input.mi.dwFlags = up_flags; | |
| 342 if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT))) | |
| 343 return false; | |
| 344 | |
| 345 if (dispatcher.get()) | |
| 346 dispatcher->AddRef(); | |
| 347 | |
| 348 return true; | |
| 349 } | |
| 350 | |
| 351 } // namespace internal | |
| 352 } // namespace ui_controls | |
| OLD | NEW |