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

Side by Side Diff: chrome/browser/automation/ui_controls_mac.mm

Issue 8212006: base::Bind: Cleanup in automation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Review fixes. Created 9 years, 2 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
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 #import <Cocoa/Cocoa.h> 7 #import <Cocoa/Cocoa.h>
8 #include <mach/mach_time.h> 8 #include <mach/mach_time.h>
9 #include <vector> 9 #include <vector>
10 10
11 #include "base/callback.h"
11 #include "base/message_loop.h" 12 #include "base/message_loop.h"
12 #include "chrome/browser/automation/ui_controls_internal.h" 13 #include "chrome/browser/automation/ui_controls_internal.h"
13 #include "content/browser/browser_thread.h" 14 #include "content/browser/browser_thread.h"
14 #include "ui/base/keycodes/keyboard_code_conversion_mac.h" 15 #include "ui/base/keycodes/keyboard_code_conversion_mac.h"
15 16
16 // Implementation details: We use [NSApplication sendEvent:] instead 17 // Implementation details: We use [NSApplication sendEvent:] instead
17 // of [NSApplication postEvent:atStart:] so that the event gets sent 18 // of [NSApplication postEvent:atStart:] so that the event gets sent
18 // immediately. This lets us run the post-event task right 19 // immediately. This lets us run the post-event task right
19 // immediately as well. Unfortunately I cannot subclass NSEvent (it's 20 // immediately as well. Unfortunately I cannot subclass NSEvent (it's
20 // probably a class cluster) to allow other easy answers. For 21 // probably a class cluster) to allow other easy answers. For
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
180 events->push_back(event); 181 events->push_back(event);
181 } 182 }
182 if (control) { 183 if (control) {
183 flags &= ~NSControlKeyMask; 184 flags &= ~NSControlKeyMask;
184 event = SynthesizeKeyEvent(window, false, ui::VKEY_CONTROL, flags); 185 event = SynthesizeKeyEvent(window, false, ui::VKEY_CONTROL, flags);
185 DCHECK(event); 186 DCHECK(event);
186 events->push_back(event); 187 events->push_back(event);
187 } 188 }
188 } 189 }
189 190
190 // A task class to watch for the event queue. The specific task will be fired 191 // A helper function to watch for the event queue. The specific task will be
191 // when there is no more event in the queue. 192 // fired when there is no more event in the queue.
192 class EventQueueWatcher : public Task { 193 void EventQueueWatcher(const base::Closure& task) {
193 public: 194 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
194 EventQueueWatcher(Task* task) : task_(task) {} 195 untilDate:nil
195 196 inMode:NSDefaultRunLoopMode
196 virtual ~EventQueueWatcher() {} 197 dequeue:NO];
197 198 // If there is still event in the queue, then we need to check again.
198 virtual void Run() { 199 if (event) {
199 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask 200 MessageLoop::current()->PostTask(
200 untilDate:nil 201 FROM_HERE,
201 inMode:NSDefaultRunLoopMode 202 base::Bind(&EventQueueWatcher, task));
202 dequeue:NO]; 203 } else {
203 // If there is still event in the queue, then we need to check again. 204 MessageLoop::current()->PostTask(FROM_HERE, task);
204 if (event)
205 MessageLoop::current()->PostTask(FROM_HERE, new EventQueueWatcher(task_));
206 else
207 MessageLoop::current()->PostTask(FROM_HERE, task_);
208 } 205 }
209 206 }
210 private:
211 Task* task_;
212 };
213 207
214 // Stores the current mouse location on the screen. So that we can use it 208 // Stores the current mouse location on the screen. So that we can use it
215 // when firing keyboard and mouse click events. 209 // when firing keyboard and mouse click events.
216 NSPoint g_mouse_location = { 0, 0 }; 210 NSPoint g_mouse_location = { 0, 0 };
217 211
218 } // anonymous namespace 212 } // anonymous namespace
219 213
220
221 namespace ui_controls { 214 namespace ui_controls {
222 215
223 bool SendKeyPress(gfx::NativeWindow window, 216 bool SendKeyPress(gfx::NativeWindow window,
224 ui::KeyboardCode key, 217 ui::KeyboardCode key,
225 bool control, 218 bool control,
226 bool shift, 219 bool shift,
227 bool alt, 220 bool alt,
228 bool command) { 221 bool command) {
229 return SendKeyPressNotifyWhenDone(window, key, 222 return SendKeyPressNotifyWhenDone(window, key,
230 control, shift, alt, command, 223 control, shift, alt, command,
231 NULL); 224 MessageLoop::QuitClosure());
232 } 225 }
233 226
234 // Win and Linux implement a SendKeyPress() this as a 227 // Win and Linux implement a SendKeyPress() this as a
235 // SendKeyPressAndRelease(), so we should as well (despite the name). 228 // SendKeyPressAndRelease(), so we should as well (despite the name).
236 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, 229 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
237 ui::KeyboardCode key, 230 ui::KeyboardCode key,
238 bool control, 231 bool control,
239 bool shift, 232 bool shift,
240 bool alt, 233 bool alt,
241 bool command, 234 bool command,
242 Task* task) { 235 const base::Closure& task) {
243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
244 237
245 std::vector<NSEvent*> events; 238 std::vector<NSEvent*> events;
246 SynthesizeKeyEventsSequence( 239 SynthesizeKeyEventsSequence(
247 window, key, control, shift, alt, command, &events); 240 window, key, control, shift, alt, command, &events);
248 241
249 // TODO(suzhe): Using [NSApplication postEvent:atStart:] here causes 242 // TODO(suzhe): Using [NSApplication postEvent:atStart:] here causes
250 // BrowserKeyEventsTest.CommandKeyEvents to fail. See http://crbug.com/49270 243 // BrowserKeyEventsTest.CommandKeyEvents to fail. See http://crbug.com/49270
251 // But using [NSApplication sendEvent:] should be safe for keyboard events, 244 // But using [NSApplication sendEvent:] should be safe for keyboard events,
252 // because until now, no code wants to retrieve the next event when handling 245 // because until now, no code wants to retrieve the next event when handling
253 // a keyboard event. 246 // a keyboard event.
254 for (std::vector<NSEvent*>::iterator iter = events.begin(); 247 for (std::vector<NSEvent*>::iterator iter = events.begin();
255 iter != events.end(); ++iter) 248 iter != events.end(); ++iter)
256 [[NSApplication sharedApplication] sendEvent:*iter]; 249 [[NSApplication sharedApplication] sendEvent:*iter];
257 250
258 if (task) 251 if (!task.is_null()) {
259 MessageLoop::current()->PostTask(FROM_HERE, new EventQueueWatcher(task)); 252 MessageLoop::current()->PostTask(
253 FROM_HERE, base::Bind(&EventQueueWatcher, task));
254 }
260 255
261 return true; 256 return true;
262 } 257 }
263 258
264 bool SendMouseMove(long x, long y) { 259 bool SendMouseMove(long x, long y) {
265 return SendMouseMoveNotifyWhenDone(x, y, NULL); 260 return SendMouseMoveNotifyWhenDone(x, y, NULL);
csilv 2011/10/11 21:48:00 Can't pass null for last argument, so probably cha
James Hawkins 2011/10/11 21:54:20 Done. Used base::Closure() here and elsewhere.
266 } 261 }
267 262
268 // Input position is in screen coordinates. However, NSMouseMoved 263 // Input position is in screen coordinates. However, NSMouseMoved
269 // events require them window-relative, so we adjust. We *DO* flip 264 // events require them window-relative, so we adjust. We *DO* flip
270 // the coordinate space, so input events can be the same for all 265 // the coordinate space, so input events can be the same for all
271 // platforms. E.g. (0,0) is upper-left. 266 // platforms. E.g. (0,0) is upper-left.
272 bool SendMouseMoveNotifyWhenDone(long x, long y, Task* task) { 267 bool SendMouseMoveNotifyWhenDone(long x, long y, Task* task) {
csilv 2011/10/11 21:48:00 Task* -> const base::Closure&
James Hawkins 2011/10/11 21:54:20 Done.
273 NSWindow* window = [[NSApplication sharedApplication] keyWindow]; 268 NSWindow* window = [[NSApplication sharedApplication] keyWindow];
274 CGFloat screenHeight = 269 CGFloat screenHeight =
275 [[[NSScreen screens] objectAtIndex:0] frame].size.height; 270 [[[NSScreen screens] objectAtIndex:0] frame].size.height;
276 g_mouse_location = NSMakePoint(x, screenHeight - y); // flip! 271 g_mouse_location = NSMakePoint(x, screenHeight - y); // flip!
277 NSPoint pointInWindow = g_mouse_location; 272 NSPoint pointInWindow = g_mouse_location;
278 if (window) 273 if (window)
279 pointInWindow = [window convertScreenToBase:pointInWindow]; 274 pointInWindow = [window convertScreenToBase:pointInWindow];
280 NSTimeInterval timestamp = TimeIntervalSinceSystemStartup(); 275 NSTimeInterval timestamp = TimeIntervalSinceSystemStartup();
281 276
282 NSEvent* event = 277 NSEvent* event =
283 [NSEvent mouseEventWithType:NSMouseMoved 278 [NSEvent mouseEventWithType:NSMouseMoved
284 location:pointInWindow 279 location:pointInWindow
285 modifierFlags:0 280 modifierFlags:0
286 timestamp:timestamp 281 timestamp:timestamp
287 windowNumber:[window windowNumber] 282 windowNumber:[window windowNumber]
288 context:nil 283 context:nil
289 eventNumber:0 284 eventNumber:0
290 clickCount:0 285 clickCount:0
291 pressure:0.0]; 286 pressure:0.0];
292 [[NSApplication sharedApplication] postEvent:event atStart:NO]; 287 [[NSApplication sharedApplication] postEvent:event atStart:NO];
293 288
294 if (task) 289 if (!task.is_null()) {
295 MessageLoop::current()->PostTask(FROM_HERE, new EventQueueWatcher(task)); 290 MessageLoop::current()->PostTask(
291 FROM_HERE, base::Bind(&EventQueueWatcher, task));
292 }
296 293
297 return true; 294 return true;
298 } 295 }
299 296
300 bool SendMouseEvents(MouseButton type, int state) { 297 bool SendMouseEvents(MouseButton type, int state) {
301 return SendMouseEventsNotifyWhenDone(type, state, NULL); 298 return SendMouseEventsNotifyWhenDone(type, state, NULL);
csilv 2011/10/11 21:48:00 Can't pass null for last argument, see previous co
James Hawkins 2011/10/11 21:54:20 Done.
302 } 299 }
303 300
304 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task) { 301 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task) {
csilv 2011/10/11 21:48:00 Task* -> const base::Closure&
James Hawkins 2011/10/11 21:54:20 Done.
305 // On windows it appears state can be (UP|DOWN). It is unclear if 302 // On windows it appears state can be (UP|DOWN). It is unclear if
306 // that'll happen here but prepare for it just in case. 303 // that'll happen here but prepare for it just in case.
307 if (state == (UP|DOWN)) { 304 if (state == (UP|DOWN)) {
308 return (SendMouseEventsNotifyWhenDone(type, DOWN, NULL) && 305 return (SendMouseEventsNotifyWhenDone(type, DOWN, NULL) &&
309 SendMouseEventsNotifyWhenDone(type, UP, task)); 306 SendMouseEventsNotifyWhenDone(type, UP, task));
310 } 307 }
311 NSEventType etype = 0; 308 NSEventType etype = 0;
312 if (type == LEFT) { 309 if (type == LEFT) {
313 if (state == UP) { 310 if (state == UP) {
314 etype = NSLeftMouseUp; 311 etype = NSLeftMouseUp;
(...skipping 25 matching lines...) Expand all
340 location:pointInWindow 337 location:pointInWindow
341 modifierFlags:0 338 modifierFlags:0
342 timestamp:TimeIntervalSinceSystemStartup() 339 timestamp:TimeIntervalSinceSystemStartup()
343 windowNumber:[window windowNumber] 340 windowNumber:[window windowNumber]
344 context:nil 341 context:nil
345 eventNumber:0 342 eventNumber:0
346 clickCount:1 343 clickCount:1
347 pressure:(state == DOWN ? 1.0 : 0.0 )]; 344 pressure:(state == DOWN ? 1.0 : 0.0 )];
348 [[NSApplication sharedApplication] postEvent:event atStart:NO]; 345 [[NSApplication sharedApplication] postEvent:event atStart:NO];
349 346
350 if (task) 347 if (!task.is_null()) {
351 MessageLoop::current()->PostTask(FROM_HERE, new EventQueueWatcher(task)); 348 MessageLoop::current()->PostTask(
349 FROM_HERE, base::Bind(&EventQueueWatcher, task));
350 }
352 351
353 return true; 352 return true;
354 } 353 }
355 354
356 bool SendMouseClick(MouseButton type) { 355 bool SendMouseClick(MouseButton type) {
357 return SendMouseEventsNotifyWhenDone(type, UP|DOWN, NULL); 356 return SendMouseEventsNotifyWhenDone(type, UP|DOWN, NULL);
csilv 2011/10/11 21:48:00 Can't pass null for last argument, see previous co
James Hawkins 2011/10/11 21:54:20 Done.
358 } 357 }
359 358
360 void MoveMouseToCenterAndPress( 359 void MoveMouseToCenterAndPress(
361 NSView* view, 360 NSView* view,
362 MouseButton button, 361 MouseButton button,
363 int state, 362 int state,
364 Task* task) { 363 const base::Closure& task) {
365 DCHECK(view); 364 DCHECK(view);
366 NSWindow* window = [view window]; 365 NSWindow* window = [view window];
367 DCHECK(window); 366 DCHECK(window);
368 NSScreen* screen = [window screen]; 367 NSScreen* screen = [window screen];
369 DCHECK(screen); 368 DCHECK(screen);
370 369
371 // Converts the center position of the view into the coordinates accepted 370 // Converts the center position of the view into the coordinates accepted
372 // by SendMouseMoveNotifyWhenDone() method. 371 // by SendMouseMoveNotifyWhenDone() method.
373 NSRect bounds = [view bounds]; 372 NSRect bounds = [view bounds];
374 NSPoint center = NSMakePoint(NSMidX(bounds), NSMidY(bounds)); 373 NSPoint center = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
375 center = [view convertPoint:center toView:nil]; 374 center = [view convertPoint:center toView:nil];
376 center = [window convertBaseToScreen:center]; 375 center = [window convertBaseToScreen:center];
377 center = NSMakePoint(center.x, [screen frame].size.height - center.y); 376 center = NSMakePoint(center.x, [screen frame].size.height - center.y);
378 377
379 SendMouseMoveNotifyWhenDone(center.x, center.y, 378 SendMouseMoveNotifyWhenDone(center.x, center.y,
380 new ClickTask(button, state, task)); 379 ClickTask(button, state, task));
csilv 2011/10/11 21:48:00 ClickTask(button, state, task) ->base::Bind(&ui_co
James Hawkins 2011/10/11 21:54:20 Done.
381 } 380 }
382 381
383 } // ui_controls 382 } // ui_controls
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698