Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "chrome/browser/automation/ui_controls.h" | 5 #include "chrome/browser/automation/ui_controls.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback.h" | |
| 7 #include "base/logging.h" | 9 #include "base/logging.h" |
| 8 #include "base/memory/ref_counted.h" | 10 #include "base/memory/ref_counted.h" |
| 9 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
| 10 #include "base/task.h" | 12 #include "base/task.h" |
| 11 #include "ui/base/keycodes/keyboard_codes.h" | 13 #include "ui/base/keycodes/keyboard_codes.h" |
| 12 #include "ui/base/keycodes/keyboard_code_conversion_win.h" | 14 #include "ui/base/keycodes/keyboard_code_conversion_win.h" |
| 13 #include "views/view.h" | 15 #include "views/view.h" |
| 14 | 16 |
| 15 namespace ui_controls { | 17 namespace ui_controls { |
| 16 | 18 |
| 17 namespace { | 19 namespace { |
| 18 | 20 |
| 19 // InputDispatcher ------------------------------------------------------------ | 21 // InputDispatcher ------------------------------------------------------------ |
| 20 | 22 |
| 21 // InputDispatcher is used to listen for a mouse/keyboard event. When the | 23 // InputDispatcher is used to listen for a mouse/keyboard event. When the |
| 22 // appropriate event is received the task is notified. | 24 // appropriate event is received the task is notified. |
| 23 class InputDispatcher : public base::RefCounted<InputDispatcher> { | 25 class InputDispatcher : public base::RefCounted<InputDispatcher> { |
| 24 public: | 26 public: |
| 25 InputDispatcher(Task* task, WPARAM message_waiting_for); | 27 InputDispatcher(base::Closure& task, WPARAM message_waiting_for); |
| 26 | 28 |
| 27 // Invoked from the hook. If mouse_message matches message_waiting_for_ | 29 // Invoked from the hook. If mouse_message matches message_waiting_for_ |
| 28 // MatchingMessageFound is invoked. | 30 // MatchingMessageFound is invoked. |
| 29 void DispatchedMessage(WPARAM mouse_message); | 31 void DispatchedMessage(WPARAM mouse_message); |
| 30 | 32 |
| 31 // Invoked when a matching event is found. Uninstalls the hook and schedules | 33 // Invoked when a matching event is found. Uninstalls the hook and schedules |
| 32 // an event that notifies the task. | 34 // an event that notifies the task. |
| 33 void MatchingMessageFound(); | 35 void MatchingMessageFound(); |
| 34 | 36 |
| 35 private: | 37 private: |
| 36 friend class base::RefCounted<InputDispatcher>; | 38 friend class base::RefCounted<InputDispatcher>; |
| 37 | 39 |
| 38 ~InputDispatcher(); | 40 ~InputDispatcher(); |
| 39 | 41 |
| 40 // Notifies the task and release this (which should delete it). | 42 // Notifies the task and release this (which should delete it). |
| 41 void NotifyTask(); | 43 void NotifyTask(); |
| 42 | 44 |
| 43 // The task we notify. | 45 // The task we notify. |
| 44 scoped_ptr<Task> task_; | 46 base::Closure task_; |
| 45 | 47 |
| 46 // Message we're waiting for. Not used for keyboard events. | 48 // Message we're waiting for. Not used for keyboard events. |
| 47 const WPARAM message_waiting_for_; | 49 const WPARAM message_waiting_for_; |
| 48 | 50 |
| 49 DISALLOW_COPY_AND_ASSIGN(InputDispatcher); | 51 DISALLOW_COPY_AND_ASSIGN(InputDispatcher); |
| 50 }; | 52 }; |
| 51 | 53 |
| 52 // Have we installed the hook? | 54 // Have we installed the hook? |
| 53 bool installed_hook_ = false; | 55 bool installed_hook_ = false; |
| 54 | 56 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 100 | 102 |
| 101 // Uninstalls the hook set in InstallHook. | 103 // Uninstalls the hook set in InstallHook. |
| 102 void UninstallHook(InputDispatcher* dispatcher) { | 104 void UninstallHook(InputDispatcher* dispatcher) { |
| 103 if (current_dispatcher_ == dispatcher) { | 105 if (current_dispatcher_ == dispatcher) { |
| 104 installed_hook_ = false; | 106 installed_hook_ = false; |
| 105 current_dispatcher_ = NULL; | 107 current_dispatcher_ = NULL; |
| 106 UnhookWindowsHookEx(next_hook_); | 108 UnhookWindowsHookEx(next_hook_); |
| 107 } | 109 } |
| 108 } | 110 } |
| 109 | 111 |
| 110 InputDispatcher::InputDispatcher(Task* task, UINT message_waiting_for) | 112 InputDispatcher::InputDispatcher(const base::Closure& task, |
| 113 UINT message_waiting_for) | |
| 111 : task_(task), message_waiting_for_(message_waiting_for) { | 114 : task_(task), message_waiting_for_(message_waiting_for) { |
| 112 InstallHook(this, message_waiting_for == WM_KEYUP); | 115 InstallHook(this, message_waiting_for == WM_KEYUP); |
| 113 } | 116 } |
| 114 | 117 |
| 115 InputDispatcher::~InputDispatcher() { | 118 InputDispatcher::~InputDispatcher() { |
| 116 // Make sure the hook isn't installed. | 119 // Make sure the hook isn't installed. |
| 117 UninstallHook(this); | 120 UninstallHook(this); |
| 118 } | 121 } |
| 119 | 122 |
| 120 void InputDispatcher::DispatchedMessage(WPARAM message) { | 123 void InputDispatcher::DispatchedMessage(WPARAM message) { |
| 121 if (message == message_waiting_for_) | 124 if (message == message_waiting_for_) |
| 122 MatchingMessageFound(); | 125 MatchingMessageFound(); |
| 123 } | 126 } |
| 124 | 127 |
| 125 void InputDispatcher::MatchingMessageFound() { | 128 void InputDispatcher::MatchingMessageFound() { |
| 126 UninstallHook(this); | 129 UninstallHook(this); |
| 127 // At the time we're invoked the event has not actually been processed. | 130 // At the time we're invoked the event has not actually been processed. |
| 128 // Use PostTask to make sure the event has been processed before notifying. | 131 // Use PostTask to make sure the event has been processed before notifying. |
| 129 MessageLoop::current()->PostDelayedTask( | 132 MessageLoop::current()->PostTask( |
| 130 FROM_HERE, NewRunnableMethod(this, &InputDispatcher::NotifyTask), 0); | 133 FROM_HERE, base::Bind(&InputDispatcher::NotifyTask, this)); |
| 131 } | 134 } |
| 132 | 135 |
| 133 void InputDispatcher::NotifyTask() { | 136 void InputDispatcher::NotifyTask() { |
| 134 task_->Run(); | 137 task_.Run(); |
| 135 Release(); | 138 Release(); |
| 136 } | 139 } |
| 137 | 140 |
| 138 // Private functions ---------------------------------------------------------- | 141 // Private functions ---------------------------------------------------------- |
| 139 | 142 |
| 140 // Populate the INPUT structure with the appropriate keyboard event | 143 // Populate the INPUT structure with the appropriate keyboard event |
| 141 // parameters required by SendInput | 144 // parameters required by SendInput |
| 142 bool FillKeyboardInput(ui::KeyboardCode key, INPUT* input, bool key_up) { | 145 bool FillKeyboardInput(ui::KeyboardCode key, INPUT* input, bool key_up) { |
| 143 memset(input, 0, sizeof(INPUT)); | 146 memset(input, 0, sizeof(INPUT)); |
| 144 input->type = INPUT_KEYBOARD; | 147 input->type = INPUT_KEYBOARD; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 157 return false; | 160 return false; |
| 158 | 161 |
| 159 if (!::SendInput(1, &input, sizeof(INPUT))) | 162 if (!::SendInput(1, &input, sizeof(INPUT))) |
| 160 return false; | 163 return false; |
| 161 | 164 |
| 162 return true; | 165 return true; |
| 163 } | 166 } |
| 164 | 167 |
| 165 bool SendKeyPressImpl(ui::KeyboardCode key, | 168 bool SendKeyPressImpl(ui::KeyboardCode key, |
| 166 bool control, bool shift, bool alt, | 169 bool control, bool shift, bool alt, |
| 167 Task* task) { | 170 const base::Closure& task) { |
| 168 scoped_refptr<InputDispatcher> dispatcher( | 171 scoped_refptr<InputDispatcher> dispatcher( |
| 169 task ? new InputDispatcher(task, WM_KEYUP) : NULL); | 172 !task.is_null() ? new InputDispatcher(task, WM_KEYUP) : NULL); |
| 170 | 173 |
| 171 // If a pop-up menu is open, it won't receive events sent using SendInput. | 174 // If a pop-up menu is open, it won't receive events sent using SendInput. |
| 172 // Check for a pop-up menu using its window class (#32768) and if one | 175 // Check for a pop-up menu using its window class (#32768) and if one |
| 173 // exists, send the key event directly there. | 176 // exists, send the key event directly there. |
| 174 HWND popup_menu = ::FindWindow(L"#32768", 0); | 177 HWND popup_menu = ::FindWindow(L"#32768", 0); |
| 175 if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) { | 178 if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) { |
| 176 WPARAM w_param = ui::WindowsKeyCodeForKeyboardCode(key); | 179 WPARAM w_param = ui::WindowsKeyCodeForKeyboardCode(key); |
| 177 LPARAM l_param = 0; | 180 LPARAM l_param = 0; |
| 178 ::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param); | 181 ::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param); |
| 179 ::SendMessage(popup_menu, WM_KEYUP, w_param, l_param); | 182 ::SendMessage(popup_menu, WM_KEYUP, w_param, l_param); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 232 | 235 |
| 233 if (::SendInput(i, input, sizeof(INPUT)) != i) | 236 if (::SendInput(i, input, sizeof(INPUT)) != i) |
| 234 return false; | 237 return false; |
| 235 | 238 |
| 236 if (dispatcher.get()) | 239 if (dispatcher.get()) |
| 237 dispatcher->AddRef(); | 240 dispatcher->AddRef(); |
| 238 | 241 |
| 239 return true; | 242 return true; |
| 240 } | 243 } |
| 241 | 244 |
| 242 bool SendMouseMoveImpl(long x, long y, Task* task) { | 245 bool SendMouseMoveImpl(long x, long y, const base::Closure& task) { |
| 243 // First check if the mouse is already there. | 246 // First check if the mouse is already there. |
| 244 POINT current_pos; | 247 POINT current_pos; |
| 245 ::GetCursorPos(¤t_pos); | 248 ::GetCursorPos(¤t_pos); |
| 246 if (x == current_pos.x && y == current_pos.y) { | 249 if (x == current_pos.x && y == current_pos.y) { |
| 247 if (task) | 250 if (!task.is_null()) |
| 248 MessageLoop::current()->PostTask(FROM_HERE, task); | 251 MessageLoop::current()->PostTask(FROM_HERE, task); |
| 249 return true; | 252 return true; |
| 250 } | 253 } |
| 251 | 254 |
| 252 INPUT input = { 0 }; | 255 INPUT input = { 0 }; |
| 253 | 256 |
| 254 int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1; | 257 int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1; |
| 255 int screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1; | 258 int screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1; |
| 256 LONG pixel_x = static_cast<LONG>(x * (65535.0f / screen_width)); | 259 LONG pixel_x = static_cast<LONG>(x * (65535.0f / screen_width)); |
| 257 LONG pixel_y = static_cast<LONG>(y * (65535.0f / screen_height)); | 260 LONG pixel_y = static_cast<LONG>(y * (65535.0f / screen_height)); |
| 258 | 261 |
| 259 input.type = INPUT_MOUSE; | 262 input.type = INPUT_MOUSE; |
| 260 input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; | 263 input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; |
| 261 input.mi.dx = pixel_x; | 264 input.mi.dx = pixel_x; |
| 262 input.mi.dy = pixel_y; | 265 input.mi.dy = pixel_y; |
| 263 | 266 |
| 264 scoped_refptr<InputDispatcher> dispatcher( | 267 scoped_refptr<InputDispatcher> dispatcher( |
| 265 task ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL); | 268 !task.is_null() ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL); |
| 266 | 269 |
| 267 if (!::SendInput(1, &input, sizeof(INPUT))) | 270 if (!::SendInput(1, &input, sizeof(INPUT))) |
| 268 return false; | 271 return false; |
| 269 | 272 |
| 270 if (dispatcher.get()) | 273 if (dispatcher.get()) |
| 271 dispatcher->AddRef(); | 274 dispatcher->AddRef(); |
| 272 | 275 |
| 273 return true; | 276 return true; |
| 274 } | 277 } |
| 275 | 278 |
| 276 bool SendMouseEventsImpl(MouseButton type, int state, Task* task) { | 279 bool SendMouseEventsImpl(MouseButton type, int state, |
| 280 const base::Closure& task) { | |
| 277 DWORD down_flags = MOUSEEVENTF_ABSOLUTE; | 281 DWORD down_flags = MOUSEEVENTF_ABSOLUTE; |
| 278 DWORD up_flags = MOUSEEVENTF_ABSOLUTE; | 282 DWORD up_flags = MOUSEEVENTF_ABSOLUTE; |
| 279 UINT last_event; | 283 UINT last_event; |
| 280 | 284 |
| 281 switch (type) { | 285 switch (type) { |
| 282 case LEFT: | 286 case LEFT: |
| 283 down_flags |= MOUSEEVENTF_LEFTDOWN; | 287 down_flags |= MOUSEEVENTF_LEFTDOWN; |
| 284 up_flags |= MOUSEEVENTF_LEFTUP; | 288 up_flags |= MOUSEEVENTF_LEFTUP; |
| 285 last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN; | 289 last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN; |
| 286 break; | 290 break; |
| 287 | 291 |
| 288 case MIDDLE: | 292 case MIDDLE: |
| 289 down_flags |= MOUSEEVENTF_MIDDLEDOWN; | 293 down_flags |= MOUSEEVENTF_MIDDLEDOWN; |
| 290 up_flags |= MOUSEEVENTF_MIDDLEUP; | 294 up_flags |= MOUSEEVENTF_MIDDLEUP; |
| 291 last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN; | 295 last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN; |
| 292 break; | 296 break; |
| 293 | 297 |
| 294 case RIGHT: | 298 case RIGHT: |
| 295 down_flags |= MOUSEEVENTF_RIGHTDOWN; | 299 down_flags |= MOUSEEVENTF_RIGHTDOWN; |
| 296 up_flags |= MOUSEEVENTF_RIGHTUP; | 300 up_flags |= MOUSEEVENTF_RIGHTUP; |
| 297 last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN; | 301 last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN; |
| 298 break; | 302 break; |
| 299 | 303 |
| 300 default: | 304 default: |
| 301 NOTREACHED(); | 305 NOTREACHED(); |
| 302 return false; | 306 return false; |
| 303 } | 307 } |
| 304 | 308 |
| 305 scoped_refptr<InputDispatcher> dispatcher( | 309 scoped_refptr<InputDispatcher> dispatcher( |
| 306 task ? new InputDispatcher(task, last_event) : NULL); | 310 !task.is_null() ? new InputDispatcher(task, last_event) : NULL); |
| 307 | 311 |
| 308 INPUT input = { 0 }; | 312 INPUT input = { 0 }; |
| 309 input.type = INPUT_MOUSE; | 313 input.type = INPUT_MOUSE; |
| 310 input.mi.dwFlags = down_flags; | 314 input.mi.dwFlags = down_flags; |
| 311 if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT))) | 315 if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT))) |
| 312 return false; | 316 return false; |
| 313 | 317 |
| 314 input.mi.dwFlags = up_flags; | 318 input.mi.dwFlags = up_flags; |
| 315 if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT))) | 319 if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT))) |
| 316 return false; | 320 return false; |
| 317 | 321 |
| 318 if (dispatcher.get()) | 322 if (dispatcher.get()) |
| 319 dispatcher->AddRef(); | 323 dispatcher->AddRef(); |
| 320 | 324 |
| 321 return true; | 325 return true; |
| 322 } | 326 } |
| 323 | 327 |
| 324 } // namespace | 328 } // namespace |
| 325 | 329 |
| 326 // public functions ----------------------------------------------------------- | 330 // public functions ----------------------------------------------------------- |
| 327 | 331 |
| 328 bool SendKeyPress(gfx::NativeWindow window, | 332 bool SendKeyPress(gfx::NativeWindow window, |
| 329 ui::KeyboardCode key, | 333 ui::KeyboardCode key, |
| 330 bool control, | 334 bool control, |
| 331 bool shift, | 335 bool shift, |
| 332 bool alt, | 336 bool alt, |
| 333 bool command) { | 337 bool command) { |
| 334 DCHECK(!command); // No command key on Windows | 338 DCHECK(!command); // No command key on Windows |
| 335 return SendKeyPressImpl(key, control, shift, alt, NULL); | 339 return SendKeyPressImpl(key, control, shift, alt, base::Closure()); |
| 336 } | 340 } |
| 337 | 341 |
| 338 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, | 342 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, |
| 339 ui::KeyboardCode key, | 343 ui::KeyboardCode key, |
| 340 bool control, | 344 bool control, |
| 341 bool shift, | 345 bool shift, |
| 342 bool alt, | 346 bool alt, |
| 343 bool command, | 347 bool command, |
| 344 Task* task) { | 348 const base::Closure& task) { |
| 345 DCHECK(!command); // No command key on Windows | 349 DCHECK(!command); // No command key on Windows |
| 346 return SendKeyPressImpl(key, control, shift, alt, task); | 350 return SendKeyPressImpl(key, control, shift, alt, task); |
| 347 } | 351 } |
| 348 | 352 |
| 349 bool SendMouseMove(long x, long y) { | 353 bool SendMouseMove(long x, long y) { |
| 350 return SendMouseMoveImpl(x, y, NULL); | 354 return SendMouseMoveImpl(x, y, base::Closure()); |
| 351 } | 355 } |
| 352 | 356 |
| 353 bool SendMouseMoveNotifyWhenDone(long x, long y, Task* task) { | 357 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { |
| 354 return SendMouseMoveImpl(x, y, task); | 358 return SendMouseMoveImpl(x, y, task); |
| 355 } | 359 } |
| 356 | 360 |
| 357 bool SendMouseEvents(MouseButton type, int state) { | 361 bool SendMouseEvents(MouseButton type, int state) { |
| 358 return SendMouseEventsImpl(type, state, NULL); | 362 return SendMouseEventsImpl(type, state, base::Closure()); |
| 359 } | 363 } |
| 360 | 364 |
| 361 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task) { | 365 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, |
| 366 base::Closure& task) { | |
|
awong
2011/10/11 01:18:04
const base::Closure&
James Hawkins
2011/10/11 21:11:22
Done.
| |
| 362 return SendMouseEventsImpl(type, state, task); | 367 return SendMouseEventsImpl(type, state, task); |
| 363 } | 368 } |
| 364 | 369 |
| 365 bool SendMouseClick(MouseButton type) { | 370 bool SendMouseClick(MouseButton type) { |
| 366 return SendMouseEventsImpl(type, UP | DOWN, NULL); | 371 return SendMouseEventsImpl(type, UP | DOWN, base::Closure()); |
| 367 } | 372 } |
| 368 | 373 |
| 369 void MoveMouseToCenterAndPress(views::View* view, | 374 void MoveMouseToCenterAndPress(views::View* view, |
| 370 MouseButton button, | 375 MouseButton button, |
| 371 int state, | 376 int state, |
| 372 Task* task) { | 377 base::Closure& task) { |
| 373 DCHECK(view); | 378 DCHECK(view); |
| 374 DCHECK(view->GetWidget()); | 379 DCHECK(view->GetWidget()); |
| 375 gfx::Point view_center(view->width() / 2, view->height() / 2); | 380 gfx::Point view_center(view->width() / 2, view->height() / 2); |
| 376 views::View::ConvertPointToScreen(view, &view_center); | 381 views::View::ConvertPointToScreen(view, &view_center); |
| 377 SendMouseMove(view_center.x(), view_center.y()); | 382 SendMouseMove(view_center.x(), view_center.y()); |
| 378 SendMouseEventsNotifyWhenDone(button, state, task); | 383 SendMouseEventsNotifyWhenDone(button, state, task); |
| 379 } | 384 } |
| 380 | 385 |
| 381 } // ui_controls | 386 } // ui_controls |
| OLD | NEW |