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

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

Issue 9390038: Move automation/ui_controls to ui/ui_controls (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: win_aura fix, sync Created 8 years, 9 months 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
(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(&current_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
OLDNEW
« no previous file with comments | « chrome/browser/automation/ui_controls_internal.cc ('k') | chrome/browser/automation/ui_controls_mac.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698