OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "ui/base/test/ui_controls.h" | 5 #include "ui/base/test/ui_controls.h" |
6 | 6 |
7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/callback.h" | 11 #include "base/callback.h" |
| 12 #import "base/mac/foundation_util.h" |
| 13 #import "base/mac/scoped_nsobject.h" |
| 14 #import "base/mac/scoped_objc_class_swizzler.h" |
12 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" |
| 16 #include "base/thread_task_runner_handle.h" |
13 #include "ui/base/cocoa/cocoa_base_utils.h" | 17 #include "ui/base/cocoa/cocoa_base_utils.h" |
14 #include "ui/events/keycodes/keyboard_code_conversion_mac.h" | 18 #include "ui/events/keycodes/keyboard_code_conversion_mac.h" |
15 #import "ui/events/test/cocoa_test_event_utils.h" | 19 #import "ui/events/test/cocoa_test_event_utils.h" |
| 20 #import "ui/gfx/mac/coordinate_conversion.h" |
| 21 #include "ui/gfx/screen.h" |
16 | 22 |
17 // Implementation details: We use [NSApplication sendEvent:] instead | 23 // Implementation details: We use [NSApplication sendEvent:] instead |
18 // of [NSApplication postEvent:atStart:] so that the event gets sent | 24 // of [NSApplication postEvent:atStart:] so that the event gets sent |
19 // immediately. This lets us run the post-event task right | 25 // immediately. This lets us run the post-event task right |
20 // immediately as well. Unfortunately I cannot subclass NSEvent (it's | 26 // immediately as well. Unfortunately I cannot subclass NSEvent (it's |
21 // probably a class cluster) to allow other easy answers. For | 27 // probably a class cluster) to allow other easy answers. For |
22 // example, if I could subclass NSEvent, I could run the Task in it's | 28 // example, if I could subclass NSEvent, I could run the Task in it's |
23 // dealloc routine (which necessarily happens after the event is | 29 // dealloc routine (which necessarily happens after the event is |
24 // dispatched). Unlike Linux, Mac does not have message loop | 30 // dispatched). Unlike Linux, Mac does not have message loop |
25 // observer/notification. Unlike windows, I cannot post non-events | 31 // observer/notification. Unlike windows, I cannot post non-events |
(...skipping 15 matching lines...) Expand all Loading... |
41 // events causes BrowserKeyEventsTest.CommandKeyEvents to fail. | 47 // events causes BrowserKeyEventsTest.CommandKeyEvents to fail. |
42 // See http://crbug.com/49270 | 48 // See http://crbug.com/49270 |
43 // 2. On OSX 10.6, [NSEvent addLocalMonitorForEventsMatchingMask:handler:] may | 49 // 2. On OSX 10.6, [NSEvent addLocalMonitorForEventsMatchingMask:handler:] may |
44 // be used, so that we don't need to poll the event queue time to time. | 50 // be used, so that we don't need to poll the event queue time to time. |
45 | 51 |
46 using cocoa_test_event_utils::SynthesizeKeyEvent; | 52 using cocoa_test_event_utils::SynthesizeKeyEvent; |
47 using cocoa_test_event_utils::TimeIntervalSinceSystemStartup; | 53 using cocoa_test_event_utils::TimeIntervalSinceSystemStartup; |
48 | 54 |
49 namespace { | 55 namespace { |
50 | 56 |
| 57 // When enabled, all simulated mouse events will be posted to |
| 58 // the WindowServer, and the actual mouse will move on the screen. |
| 59 bool g_use_cgevents = false; |
| 60 |
51 // Stores the current mouse location on the screen. So that we can use it | 61 // Stores the current mouse location on the screen. So that we can use it |
52 // when firing keyboard and mouse click events. | 62 // when firing keyboard and mouse click events. |
53 NSPoint g_mouse_location = { 0, 0 }; | 63 NSPoint g_mouse_location = { 0, 0 }; |
54 | 64 |
| 65 // If enabled overrides results returned by +[NSEvent mouseLocation] and |
| 66 // -[NSWindow mouseLocationOutsideOfEventStream] |
| 67 NSPoint g_mouse_location_override = {0, 0}; |
| 68 bool g_mouse_location_override_enabled = false; |
| 69 |
| 70 // Stores the current pressed mouse buttons. Indexed by |
| 71 // ui_controls::MouseButton. |
| 72 bool g_mouse_button_down[3] = {false, false, false}; |
| 73 |
55 bool g_ui_controls_enabled = false; | 74 bool g_ui_controls_enabled = false; |
56 | 75 |
57 // Creates the proper sequence of autoreleased key events for a key down + up. | 76 // Creates the proper sequence of autoreleased key events for a key down + up. |
58 void SynthesizeKeyEventsSequence(NSWindow* window, | 77 void SynthesizeKeyEventsSequence(NSWindow* window, |
59 ui::KeyboardCode keycode, | 78 ui::KeyboardCode keycode, |
60 bool control, | 79 bool control, |
61 bool shift, | 80 bool shift, |
62 bool alt, | 81 bool alt, |
63 bool command, | 82 bool command, |
64 std::vector<NSEvent*>* events) { | 83 std::vector<NSEvent*>* events) { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 events->push_back(event); | 134 events->push_back(event); |
116 } | 135 } |
117 if (control) { | 136 if (control) { |
118 flags &= ~NSControlKeyMask; | 137 flags &= ~NSControlKeyMask; |
119 event = SynthesizeKeyEvent(window, false, ui::VKEY_CONTROL, flags); | 138 event = SynthesizeKeyEvent(window, false, ui::VKEY_CONTROL, flags); |
120 DCHECK(event); | 139 DCHECK(event); |
121 events->push_back(event); | 140 events->push_back(event); |
122 } | 141 } |
123 } | 142 } |
124 | 143 |
| 144 void RunTaskInTaskRunner( |
| 145 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 146 const base::Closure& task) { |
| 147 task_runner->PostTask(FROM_HERE, task); |
| 148 } |
| 149 |
125 // A helper function to watch for the event queue. The specific task will be | 150 // A helper function to watch for the event queue. The specific task will be |
126 // fired when there is no more event in the queue. | 151 // fired when there is no more event in the queue. |
127 void EventQueueWatcher(const base::Closure& task) { | 152 void EventQueueWatcher(const base::Closure& task) { |
| 153 DCHECK_EQ(dispatch_get_current_queue(), dispatch_get_main_queue()) |
| 154 << "It should be run on the UI thread, as otherwise it will always " |
| 155 "report there are no pending events"; |
128 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask | 156 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask |
129 untilDate:nil | 157 untilDate:nil |
130 inMode:NSDefaultRunLoopMode | 158 inMode:NSDefaultRunLoopMode |
131 dequeue:NO]; | 159 dequeue:NO]; |
132 // If there is still event in the queue, then we need to check again. | 160 // If there is still event in the queue, then we need to check again. |
133 if (event) { | 161 if (event) { |
134 base::MessageLoop::current()->PostTask( | 162 base::MessageLoop::current()->PostTask( |
135 FROM_HERE, | 163 FROM_HERE, |
136 base::Bind(&EventQueueWatcher, task)); | 164 base::Bind(&EventQueueWatcher, task)); |
137 } else { | 165 } else { |
(...skipping 28 matching lines...) Expand all Loading... |
166 return window; | 194 return window; |
167 } | 195 } |
168 | 196 |
169 // Note that -[NSApplication orderedWindows] won't include NSPanels. If a test | 197 // Note that -[NSApplication orderedWindows] won't include NSPanels. If a test |
170 // uses those, it will need to handle that itself. | 198 // uses those, it will need to handle that itself. |
171 return nil; | 199 return nil; |
172 } | 200 } |
173 | 201 |
174 } // namespace | 202 } // namespace |
175 | 203 |
| 204 // Since CGEvents take some time to reach -[NSApplication sendEvent:] we use |
| 205 // this class to wait for the events to start being processed before sending |
| 206 // finish notifications. |
| 207 class EventMonitor { |
| 208 public: |
| 209 void NotifyWhenEventIsProcessed(NSEvent* event, const base::Closure& task) { |
| 210 if (g_use_cgevents) { |
| 211 CHECK([[NSApplication sharedApplication] isActive]) |
| 212 << "If we generate CGEvents and the application is not active, we'll " |
| 213 "deadlock waiting for NSMouseMoved events. Use " |
| 214 "ui_test_utils::BringBrowserWindowToFront() to activate the " |
| 215 "browser window prior to generating CGEvents."; |
| 216 } |
| 217 |
| 218 base::AutoLock auto_lock(lock_); |
| 219 tasks_.emplace_back(Task(event, task)); |
| 220 } |
| 221 |
| 222 void ProcessingEvent(NSEvent* event) { |
| 223 base::AutoLock auto_lock(lock_); |
| 224 auto it = std::find_if( |
| 225 tasks_.begin(), tasks_.end(), |
| 226 [&event](const Task& task) { return task.MatchesEvent(event); }); |
| 227 if (it != tasks_.end()) { |
| 228 it->Run(); |
| 229 tasks_.erase(it); |
| 230 } |
| 231 } |
| 232 |
| 233 static EventMonitor* Instance() { |
| 234 static EventMonitor* monitor = nullptr; |
| 235 if (!monitor) { |
| 236 monitor = new EventMonitor(); |
| 237 } |
| 238 return monitor; |
| 239 } |
| 240 |
| 241 private: |
| 242 class Task { |
| 243 public: |
| 244 Task(NSEvent* event, const base::Closure& task) |
| 245 : task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 246 event_([event retain]), |
| 247 finish_closure_(task) {} |
| 248 |
| 249 bool MatchesEvent(NSEvent* event) const { |
| 250 // When moving the window using BridgedNativeWidget::RunMoveLoop the |
| 251 // locationInWindow can be a little inconsistent with what we expect. |
| 252 // Seems that only comparing event type is fine. |
| 253 return |
| 254 [event_ type] == [event type] && [event_ subtype] == [event subtype]; |
| 255 } |
| 256 |
| 257 void Run() { |
| 258 // We get here before the event is actually processed. Run the |
| 259 // EventQueueWatcher on the main thread in order to wait for all events to |
| 260 // finish processing. |
| 261 base::MessageLoop::current()->PostTask( |
| 262 FROM_HERE, base::Bind(&EventQueueWatcher, |
| 263 base::Bind(&RunTaskInTaskRunner, task_runner_, |
| 264 finish_closure_))); |
| 265 } |
| 266 |
| 267 private: |
| 268 // Events could be spawned on background threads. Be sure to invoke the |
| 269 // |finish_closure_| on an appropriate thread. |
| 270 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| 271 base::scoped_nsobject<NSEvent> event_; |
| 272 base::Closure finish_closure_; |
| 273 }; |
| 274 |
| 275 EventMonitor() |
| 276 : send_event_swizzler_( |
| 277 new base::mac::ScopedObjCClassSwizzler([NSApplication class], |
| 278 @selector(sendEvent:), |
| 279 @selector(cr_sendEvent:))) {} |
| 280 |
| 281 std::vector<Task> tasks_; |
| 282 |
| 283 // synchronizes access to the |tasks_| in case we spawn the events on a |
| 284 // different thread |
| 285 base::Lock lock_; |
| 286 |
| 287 std::unique_ptr<base::mac::ScopedObjCClassSwizzler> send_event_swizzler_; |
| 288 |
| 289 DISALLOW_COPY_AND_ASSIGN(EventMonitor); |
| 290 }; |
| 291 |
| 292 // Donates testing implementations of NSApplication methods. We can't simply |
| 293 // use -[NSEvent addLocalMonitorForEventsMatchingMask:handler:], as other event |
| 294 // monitors could have precedence, and they could filter the events before we |
| 295 // can see them. But since nobody swizzles -[NSApplication sendEvent:] we should |
| 296 // be safe. |
| 297 @interface NSApplication (TestingDonor) |
| 298 @end |
| 299 |
| 300 @implementation NSApplication (TestingDonor) |
| 301 - (void)cr_sendEvent:(NSEvent*)event { |
| 302 // Invoke the finish handler before the event is processed, since we can get |
| 303 // stuck in BridgedNativeWidget::RunMoveLoop and would never see the event |
| 304 // otherwise. |
| 305 EventMonitor::Instance()->ProcessingEvent(event); |
| 306 |
| 307 [self cr_sendEvent:event]; |
| 308 } |
| 309 @end |
| 310 |
| 311 // Donates testing implementations of NSEvent methods. |
| 312 @interface FakeNSEventTestingDonor : NSObject |
| 313 @end |
| 314 |
| 315 @implementation FakeNSEventTestingDonor |
| 316 + (NSPoint)mouseLocation { |
| 317 if (g_mouse_location_override_enabled) |
| 318 return g_mouse_location_override; |
| 319 |
| 320 return g_mouse_location; |
| 321 } |
| 322 |
| 323 + (NSUInteger)pressedMouseButtons { |
| 324 NSUInteger result = 0; |
| 325 const int buttons[3] = { |
| 326 ui_controls::LEFT, ui_controls::RIGHT, ui_controls::MIDDLE}; |
| 327 for (unsigned int i = 0; i < arraysize(buttons); ++i) { |
| 328 if (g_mouse_button_down[buttons[i]]) |
| 329 result |= (1 << i); |
| 330 } |
| 331 return result; |
| 332 } |
| 333 @end |
| 334 |
| 335 // Donates testing implementations of NSWindow methods. |
| 336 @interface FakeNSWindowTestingDonor : NSObject |
| 337 @end |
| 338 |
| 339 @implementation FakeNSWindowTestingDonor |
| 340 - (NSPoint)mouseLocationOutsideOfEventStream { |
| 341 NSWindow* window = base::mac::ObjCCastStrict<NSWindow>(self); |
| 342 if (g_mouse_location_override_enabled) |
| 343 return ui::ConvertPointFromWindowToScreen(window, |
| 344 g_mouse_location_override); |
| 345 |
| 346 return ui::ConvertPointFromWindowToScreen(window, g_mouse_location); |
| 347 } |
| 348 @end |
| 349 |
| 350 namespace { |
| 351 class NSEventSwizzler { |
| 352 public: |
| 353 static void Install() { |
| 354 static NSEventSwizzler* swizzler = nullptr; |
| 355 if (!swizzler) { |
| 356 swizzler = new NSEventSwizzler(); |
| 357 } |
| 358 } |
| 359 |
| 360 protected: |
| 361 NSEventSwizzler() |
| 362 : mouse_location_swizzler_(new base::mac::ScopedObjCClassSwizzler( |
| 363 [NSEvent class], |
| 364 [FakeNSEventTestingDonor class], |
| 365 @selector(mouseLocation))), |
| 366 pressed_mouse_buttons_swizzler_(new base::mac::ScopedObjCClassSwizzler( |
| 367 [NSEvent class], |
| 368 [FakeNSEventTestingDonor class], |
| 369 @selector(pressedMouseButtons))), |
| 370 mouse_location_outside_of_event_stream_swizzler_( |
| 371 new base::mac::ScopedObjCClassSwizzler( |
| 372 [NSWindow class], |
| 373 [FakeNSWindowTestingDonor class], |
| 374 @selector(mouseLocationOutsideOfEventStream))) {} |
| 375 |
| 376 private: |
| 377 std::unique_ptr<base::mac::ScopedObjCClassSwizzler> mouse_location_swizzler_; |
| 378 std::unique_ptr<base::mac::ScopedObjCClassSwizzler> |
| 379 pressed_mouse_buttons_swizzler_; |
| 380 std::unique_ptr<base::mac::ScopedObjCClassSwizzler> |
| 381 mouse_location_outside_of_event_stream_swizzler_; |
| 382 }; |
| 383 } // namespace |
| 384 |
176 namespace ui_controls { | 385 namespace ui_controls { |
177 | 386 |
178 void EnableUIControls() { | 387 void EnableUIControls() { |
179 g_ui_controls_enabled = true; | 388 g_ui_controls_enabled = true; |
180 } | 389 } |
181 | 390 |
182 bool IsUIControlsEnabled() { | 391 bool IsUIControlsEnabled() { |
183 return g_ui_controls_enabled; | 392 return g_ui_controls_enabled; |
184 } | 393 } |
185 | 394 |
(...skipping 11 matching lines...) Expand all Loading... |
197 | 406 |
198 // Win and Linux implement a SendKeyPress() this as a | 407 // Win and Linux implement a SendKeyPress() this as a |
199 // SendKeyPressAndRelease(), so we should as well (despite the name). | 408 // SendKeyPressAndRelease(), so we should as well (despite the name). |
200 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, | 409 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, |
201 ui::KeyboardCode key, | 410 ui::KeyboardCode key, |
202 bool control, | 411 bool control, |
203 bool shift, | 412 bool shift, |
204 bool alt, | 413 bool alt, |
205 bool command, | 414 bool command, |
206 const base::Closure& task) { | 415 const base::Closure& task) { |
| 416 DCHECK(!g_use_cgevents) << "Not implemented"; |
207 CHECK(g_ui_controls_enabled); | 417 CHECK(g_ui_controls_enabled); |
208 DCHECK(base::MessageLoopForUI::IsCurrent()); | 418 DCHECK(base::MessageLoopForUI::IsCurrent()); |
209 | 419 |
210 std::vector<NSEvent*> events; | 420 std::vector<NSEvent*> events; |
211 SynthesizeKeyEventsSequence( | 421 SynthesizeKeyEventsSequence( |
212 window, key, control, shift, alt, command, &events); | 422 window, key, control, shift, alt, command, &events); |
213 | 423 |
214 // TODO(suzhe): Using [NSApplication postEvent:atStart:] here causes | 424 // TODO(suzhe): Using [NSApplication postEvent:atStart:] here causes |
215 // BrowserKeyEventsTest.CommandKeyEvents to fail. See http://crbug.com/49270 | 425 // BrowserKeyEventsTest.CommandKeyEvents to fail. See http://crbug.com/49270 |
216 // But using [NSApplication sendEvent:] should be safe for keyboard events, | 426 // But using [NSApplication sendEvent:] should be safe for keyboard events, |
(...skipping 15 matching lines...) Expand all Loading... |
232 CHECK(g_ui_controls_enabled); | 442 CHECK(g_ui_controls_enabled); |
233 return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); | 443 return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); |
234 } | 444 } |
235 | 445 |
236 // Input position is in screen coordinates. However, NSMouseMoved | 446 // Input position is in screen coordinates. However, NSMouseMoved |
237 // events require them window-relative, so we adjust. We *DO* flip | 447 // events require them window-relative, so we adjust. We *DO* flip |
238 // the coordinate space, so input events can be the same for all | 448 // the coordinate space, so input events can be the same for all |
239 // platforms. E.g. (0,0) is upper-left. | 449 // platforms. E.g. (0,0) is upper-left. |
240 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { | 450 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { |
241 CHECK(g_ui_controls_enabled); | 451 CHECK(g_ui_controls_enabled); |
242 CGFloat screenHeight = | 452 g_mouse_location = gfx::ScreenPointToNSPoint(gfx::Point(x, y)); // flip! |
243 [[[NSScreen screens] firstObject] frame].size.height; | 453 NSEventSwizzler::Install(); |
244 g_mouse_location = NSMakePoint(x, screenHeight - y); // flip! | |
245 | 454 |
246 NSWindow* window = WindowAtCurrentMouseLocation(); | 455 NSWindow* window = WindowAtCurrentMouseLocation(); |
247 | 456 |
248 NSPoint pointInWindow = g_mouse_location; | 457 NSPoint pointInWindow = g_mouse_location; |
249 if (window) | 458 if (window) |
250 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow); | 459 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow); |
251 NSTimeInterval timestamp = TimeIntervalSinceSystemStartup(); | 460 NSTimeInterval timestamp = TimeIntervalSinceSystemStartup(); |
252 | 461 |
| 462 NSEventType event_type = NSMouseMoved; |
| 463 if (g_mouse_button_down[LEFT]) { |
| 464 event_type = NSLeftMouseDragged; |
| 465 } else if (g_mouse_button_down[RIGHT]) { |
| 466 event_type = NSRightMouseDragged; |
| 467 } else if (g_mouse_button_down[MIDDLE]) { |
| 468 event_type = NSOtherMouseDragged; |
| 469 } |
| 470 |
253 NSEvent* event = | 471 NSEvent* event = |
254 [NSEvent mouseEventWithType:NSMouseMoved | 472 [NSEvent mouseEventWithType:event_type |
255 location:pointInWindow | 473 location:pointInWindow |
256 modifierFlags:0 | 474 modifierFlags:0 |
257 timestamp:timestamp | 475 timestamp:timestamp |
258 windowNumber:[window windowNumber] | 476 windowNumber:[window windowNumber] |
259 context:nil | 477 context:nil |
260 eventNumber:0 | 478 eventNumber:0 |
261 clickCount:0 | 479 clickCount:(event_type == NSMouseMoved ? 0 : 1) |
262 pressure:0.0]; | 480 pressure:(event_type == NSMouseMoved ? 0.0 : 1.0)]; |
263 [[NSApplication sharedApplication] postEvent:event atStart:NO]; | |
264 | 481 |
265 if (!task.is_null()) { | 482 if (!task.is_null()) { |
266 base::MessageLoop::current()->PostTask( | 483 EventMonitor::Instance()->NotifyWhenEventIsProcessed(event, task); |
267 FROM_HERE, base::Bind(&EventQueueWatcher, task)); | 484 } |
| 485 |
| 486 gfx::Point gp = gfx::ScreenPointFromNSPoint(g_mouse_location); |
| 487 CGWarpMouseCursorPosition(CGPointMake(gp.x(), gp.y())); |
| 488 |
| 489 if (g_use_cgevents) { |
| 490 CGEventPost(kCGSessionEventTap, [event CGEvent]); |
| 491 } else { |
| 492 [[NSApplication sharedApplication] postEvent:event atStart:NO]; |
268 } | 493 } |
269 | 494 |
270 return true; | 495 return true; |
271 } | 496 } |
272 | 497 |
273 bool SendMouseEvents(MouseButton type, int state) { | 498 bool SendMouseEvents(MouseButton type, int state) { |
274 CHECK(g_ui_controls_enabled); | 499 CHECK(g_ui_controls_enabled); |
275 return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); | 500 return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); |
276 } | 501 } |
277 | 502 |
278 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, | 503 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, |
279 const base::Closure& task) { | 504 const base::Closure& task) { |
280 CHECK(g_ui_controls_enabled); | 505 CHECK(g_ui_controls_enabled); |
281 // On windows it appears state can be (UP|DOWN). It is unclear if | 506 // On windows it appears state can be (UP|DOWN). It is unclear if |
282 // that'll happen here but prepare for it just in case. | 507 // that'll happen here but prepare for it just in case. |
283 if (state == (UP|DOWN)) { | 508 if (state == (UP|DOWN)) { |
284 return (SendMouseEventsNotifyWhenDone(type, DOWN, base::Closure()) && | 509 return (SendMouseEventsNotifyWhenDone(type, DOWN, base::Closure()) && |
285 SendMouseEventsNotifyWhenDone(type, UP, task)); | 510 SendMouseEventsNotifyWhenDone(type, UP, task)); |
286 } | 511 } |
287 NSEventType etype = NSLeftMouseDown; | 512 NSEventType event_type = NSLeftMouseDown; |
288 if (type == LEFT) { | 513 if (type == LEFT) { |
289 if (state == UP) { | 514 if (state == UP) { |
290 etype = NSLeftMouseUp; | 515 event_type = NSLeftMouseUp; |
291 } else { | 516 } else { |
292 etype = NSLeftMouseDown; | 517 event_type = NSLeftMouseDown; |
293 } | 518 } |
294 } else if (type == MIDDLE) { | 519 } else if (type == MIDDLE) { |
295 if (state == UP) { | 520 if (state == UP) { |
296 etype = NSOtherMouseUp; | 521 event_type = NSOtherMouseUp; |
297 } else { | 522 } else { |
298 etype = NSOtherMouseDown; | 523 event_type = NSOtherMouseDown; |
299 } | 524 } |
300 } else if (type == RIGHT) { | 525 } else if (type == RIGHT) { |
301 if (state == UP) { | 526 if (state == UP) { |
302 etype = NSRightMouseUp; | 527 event_type = NSRightMouseUp; |
303 } else { | 528 } else { |
304 etype = NSRightMouseDown; | 529 event_type = NSRightMouseDown; |
305 } | 530 } |
306 } else { | 531 } else { |
| 532 NOTREACHED(); |
307 return false; | 533 return false; |
308 } | 534 } |
| 535 g_mouse_button_down[type] = state == DOWN; |
| 536 |
309 NSWindow* window = WindowAtCurrentMouseLocation(); | 537 NSWindow* window = WindowAtCurrentMouseLocation(); |
310 NSPoint pointInWindow = g_mouse_location; | 538 NSPoint pointInWindow = g_mouse_location; |
311 if (window) | 539 if (window) |
312 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow); | 540 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow); |
313 | 541 |
| 542 NSEventSwizzler::Install(); |
| 543 |
314 NSEvent* event = | 544 NSEvent* event = |
315 [NSEvent mouseEventWithType:etype | 545 [NSEvent mouseEventWithType:event_type |
316 location:pointInWindow | 546 location:pointInWindow |
317 modifierFlags:0 | 547 modifierFlags:0 |
318 timestamp:TimeIntervalSinceSystemStartup() | 548 timestamp:TimeIntervalSinceSystemStartup() |
319 windowNumber:[window windowNumber] | 549 windowNumber:[window windowNumber] |
320 context:nil | 550 context:nil |
321 eventNumber:0 | 551 eventNumber:0 |
322 clickCount:1 | 552 clickCount:1 |
323 pressure:(state == DOWN ? 1.0 : 0.0 )]; | 553 pressure:(state == DOWN ? 1.0 : 0.0 )]; |
324 [[NSApplication sharedApplication] postEvent:event atStart:NO]; | |
325 | 554 |
326 if (!task.is_null()) { | 555 if (!task.is_null()) { |
327 base::MessageLoop::current()->PostTask( | 556 EventMonitor::Instance()->NotifyWhenEventIsProcessed(event, task); |
328 FROM_HERE, base::Bind(&EventQueueWatcher, task)); | 557 } |
| 558 |
| 559 if (g_use_cgevents) { |
| 560 CGEventPost(kCGSessionEventTap, [event CGEvent]); |
| 561 } else { |
| 562 [[NSApplication sharedApplication] postEvent:event atStart:NO]; |
329 } | 563 } |
330 | 564 |
331 return true; | 565 return true; |
332 } | 566 } |
333 | 567 |
334 bool SendMouseClick(MouseButton type) { | 568 bool SendMouseClick(MouseButton type) { |
335 CHECK(g_ui_controls_enabled); | 569 CHECK(g_ui_controls_enabled); |
336 return SendMouseEventsNotifyWhenDone(type, UP|DOWN, base::Closure()); | 570 return SendMouseEventsNotifyWhenDone(type, UP|DOWN, base::Closure()); |
337 } | 571 } |
338 | 572 |
339 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { | 573 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { |
340 base::MessageLoop::current()->PostTask( | 574 base::MessageLoop::current()->PostTask( |
341 FROM_HERE, base::Bind(&EventQueueWatcher, closure)); | 575 FROM_HERE, base::Bind(&EventQueueWatcher, closure)); |
342 } | 576 } |
343 | 577 |
344 bool IsFullKeyboardAccessEnabled() { | 578 bool IsFullKeyboardAccessEnabled() { |
345 return [NSApp isFullKeyboardAccessEnabled]; | 579 return [NSApp isFullKeyboardAccessEnabled]; |
346 } | 580 } |
347 | 581 |
| 582 void SetSendMouseEventsAsCGEvents(bool enable_cgevents) { |
| 583 g_use_cgevents = enable_cgevents; |
| 584 } |
| 585 |
| 586 bool SendMouseEventsAsCGEvents() { |
| 587 return g_use_cgevents; |
| 588 } |
| 589 |
| 590 void NotifyWhenEventIsProcessed(NSEvent* event, const base::Closure& task) { |
| 591 EventMonitor::Instance()->NotifyWhenEventIsProcessed(event, task); |
| 592 } |
| 593 |
| 594 void SetMousePositionOverride(bool enable_override, const gfx::Point& p) { |
| 595 g_mouse_location_override_enabled = enable_override; |
| 596 g_mouse_location_override = gfx::ScreenPointToNSPoint(p); // flip! |
| 597 } |
| 598 |
348 } // namespace ui_controls | 599 } // namespace ui_controls |
OLD | NEW |