Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(297)

Side by Side Diff: chrome/browser/automation/ui_controls_win.cc

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

Powered by Google App Engine
This is Rietveld 408576698