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(const 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 const base::Closure& task) { |
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 const 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 |