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