Chromium Code Reviews| Index: ui/base/test/ui_controls_mac.mm |
| diff --git a/ui/base/test/ui_controls_mac.mm b/ui/base/test/ui_controls_mac.mm |
| index 15cdb7963b019d11df0e749c13239fb6a40faafe..dad3ad1f9ebacd185ddadc516bc15ad4f187371b 100644 |
| --- a/ui/base/test/ui_controls_mac.mm |
| +++ b/ui/base/test/ui_controls_mac.mm |
| @@ -9,9 +9,15 @@ |
| #include "base/bind.h" |
| #include "base/callback.h" |
| +#include "base/mac/scoped_nsautorelease_pool.h" |
| +#include "base/mac/scoped_nsobject.h" |
| +#include "base/mac/scoped_objc_class_swizzler.h" |
| #include "base/message_loop/message_loop.h" |
| +#include "base/thread_task_runner_handle.h" |
| #include "ui/events/keycodes/keyboard_code_conversion_mac.h" |
| #import "ui/events/test/cocoa_test_event_utils.h" |
| +#include "ui/gfx/mac/coordinate_conversion.h" |
| +#include "ui/gfx/screen.h" |
| // Implementation details: We use [NSApplication sendEvent:] instead |
| // of [NSApplication postEvent:atStart:] so that the event gets sent |
| @@ -50,6 +56,7 @@ namespace { |
| // Stores the current mouse location on the screen. So that we can use it |
| // when firing keyboard and mouse click events. |
| NSPoint g_mouse_location = { 0, 0 }; |
| +bool g_mouse_button_down[3] = { false, false, false }; |
|
tapted
2016/03/01 08:11:58
comment (mention that it's indexed by ui_controls:
themblsha
2016/03/09 17:40:22
Done.
|
| bool g_ui_controls_enabled = false; |
| @@ -172,6 +179,83 @@ NSWindow* WindowAtCurrentMouseLocation() { |
| } // namespace |
| +namespace { |
| +int kEventDeletedTaskRunnerKey; |
|
tapted
2016/03/01 08:11:58
can be merged with the namespace above. needs a co
themblsha
2016/03/09 17:40:22
Done.
|
| +} // namespace |
| + |
| +@interface EventDeletedTaskRunner : NSObject { |
|
tapted
2016/03/01 08:11:58
What's the reason why EventQueueWatcher couldn't s
themblsha
2016/03/01 18:41:53
I'm spawning these on a separate thread, as the ma
|
| + @private |
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| + base::Closure finish_closure_; |
| +} |
| +- (id)initWithClosure:(const base::Closure&)task; |
| +@end |
| + |
| +@implementation EventDeletedTaskRunner |
| +- (id)initWithClosure:(const base::Closure&)task { |
| + if (self = [super init]) { |
| + task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| + finish_closure_ = task; |
| + } |
| + return self; |
| +} |
| + |
| +- (void)dealloc { |
| + task_runner_->PostTask(FROM_HERE, finish_closure_); |
| + [super dealloc]; |
| +} |
| +@end |
| + |
| +// Donates testing implementations of NSEvent methods. |
| +@interface FakeNSEventTestingDonor : NSObject |
| +@end |
| + |
| +@implementation FakeNSEventTestingDonor |
| ++ (NSPoint)mouseLocation { |
| + return g_mouse_location; |
| +} |
| + |
| ++ (NSUInteger)pressedMouseButtons { |
| + NSUInteger result = 0; |
| + const int buttons[3] = { |
| + ui_controls::LEFT, ui_controls::RIGHT, ui_controls::MIDDLE}; |
| + for (unsigned int i = 0; i < arraysize(buttons); ++i) { |
| + if (g_mouse_button_down[buttons[i]]) |
| + result |= (1 << i); |
| + } |
| + return result; |
| +} |
| +@end |
| + |
| +namespace { |
| +class ScopedNSEventSwizzler { |
| + public: |
| + static void Install() { |
| + static ScopedNSEventSwizzler* swizzler = nullptr; |
| + if (!swizzler) { |
| + swizzler = new ScopedNSEventSwizzler(); |
| + } |
| + } |
| + |
| + protected: |
| + ScopedNSEventSwizzler() |
| + : mouse_location_swizzler_(new base::mac::ScopedObjCClassSwizzler( |
| + [NSEvent class], |
| + [FakeNSEventTestingDonor class], |
| + @selector(mouseLocation))), |
| + pressed_mouse_buttons_swizzler_(new base::mac::ScopedObjCClassSwizzler( |
| + [NSEvent class], |
| + [FakeNSEventTestingDonor class], |
| + @selector(pressedMouseButtons))) { |
| + } |
| + |
| + private: |
| + scoped_ptr<base::mac::ScopedObjCClassSwizzler> mouse_location_swizzler_; |
| + scoped_ptr<base::mac::ScopedObjCClassSwizzler> |
| + pressed_mouse_buttons_swizzler_; |
| +}; |
| +} // namespace |
| + |
| namespace ui_controls { |
| void EnableUIControls() { |
| @@ -202,10 +286,24 @@ bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, |
| CHECK(g_ui_controls_enabled); |
| DCHECK(base::MessageLoopForUI::IsCurrent()); |
| + // we want to destroy the autoreleased event ASAP |
|
tapted
2016/03/01 08:11:58
Comments should all be complete sentences. Capital
themblsha
2016/03/09 17:40:22
Done.
|
| + base::mac::ScopedNSAutoreleasePool pool; |
| + |
| std::vector<NSEvent*> events; |
| SynthesizeKeyEventsSequence( |
| window, key, control, shift, alt, command, &events); |
| + if (!task.is_null()) { |
| + base::scoped_nsobject<EventDeletedTaskRunner> task_runner = |
| + [[EventDeletedTaskRunner alloc] initWithClosure:task]; |
| + |
| + // event will be deleted when the last event is deleted |
| + for (std::vector<NSEvent*>::iterator iter = events.begin(); |
|
tapted
2016/03/01 08:11:58
for (NSEvent* event : events)
|
| + iter != events.end(); ++iter) |
| + objc_setAssociatedObject(*iter, &kEventDeletedTaskRunnerKey, |
| + task_runner.get(), OBJC_ASSOCIATION_RETAIN); |
| + } |
| + |
| // TODO(suzhe): Using [NSApplication postEvent:atStart:] here causes |
| // BrowserKeyEventsTest.CommandKeyEvents to fail. See http://crbug.com/49270 |
| // But using [NSApplication sendEvent:] should be safe for keyboard events, |
| @@ -215,11 +313,6 @@ bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, |
| iter != events.end(); ++iter) |
| [[NSApplication sharedApplication] sendEvent:*iter]; |
| - if (!task.is_null()) { |
| - base::MessageLoop::current()->PostTask( |
| - FROM_HERE, base::Bind(&EventQueueWatcher, task)); |
| - } |
| - |
| return true; |
| } |
| @@ -234,9 +327,8 @@ bool SendMouseMove(long x, long y) { |
| // platforms. E.g. (0,0) is upper-left. |
| bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { |
| CHECK(g_ui_controls_enabled); |
| - CGFloat screenHeight = |
| - [[[NSScreen screens] firstObject] frame].size.height; |
| - g_mouse_location = NSMakePoint(x, screenHeight - y); // flip! |
| + g_mouse_location = gfx::ScreenPointToNSPoint(gfx::Point(x, y)); // flip! |
| + ScopedNSEventSwizzler::Install(); |
| NSWindow* window = WindowAtCurrentMouseLocation(); |
| @@ -245,23 +337,37 @@ bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { |
| pointInWindow = [window convertScreenToBase:pointInWindow]; |
| NSTimeInterval timestamp = TimeIntervalSinceSystemStartup(); |
| - NSEvent* event = |
| - [NSEvent mouseEventWithType:NSMouseMoved |
| - location:pointInWindow |
| - modifierFlags:0 |
| - timestamp:timestamp |
| - windowNumber:[window windowNumber] |
| - context:nil |
| - eventNumber:0 |
| - clickCount:0 |
| - pressure:0.0]; |
| - [[NSApplication sharedApplication] postEvent:event atStart:NO]; |
| + NSEventType etype = NSMouseMoved; |
|
tapted
2016/03/01 08:11:58
etype -> event_type
themblsha
2016/03/09 17:40:22
Done.
|
| + if (g_mouse_button_down[LEFT]) { |
| + etype = NSLeftMouseDragged; |
| + } else if (g_mouse_button_down[RIGHT]) { |
| + etype = NSRightMouseDragged; |
| + } else if (g_mouse_button_down[MIDDLE]) { |
| + etype = NSOtherMouseDragged; |
| + } |
| + |
| + // we want to destroy the autoreleased event ASAP |
| + base::mac::ScopedNSAutoreleasePool pool; |
| + |
| + NSEvent* event = [NSEvent mouseEventWithType:etype |
| + location:pointInWindow |
| + modifierFlags:0 |
| + timestamp:timestamp |
| + windowNumber:[window windowNumber] |
| + context:nil |
| + eventNumber:0 |
| + clickCount:0 |
| + pressure:0.0]; |
| if (!task.is_null()) { |
| - base::MessageLoop::current()->PostTask( |
| - FROM_HERE, base::Bind(&EventQueueWatcher, task)); |
| + base::scoped_nsobject<EventDeletedTaskRunner> task_runner = |
| + [[EventDeletedTaskRunner alloc] initWithClosure:task]; |
| + objc_setAssociatedObject(event, &kEventDeletedTaskRunnerKey, |
| + task_runner.get(), OBJC_ASSOCIATION_RETAIN); |
| } |
| + [[NSApplication sharedApplication] postEvent:event atStart:NO]; |
| + |
| return true; |
| } |
| @@ -283,20 +389,26 @@ bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, |
| if (type == LEFT) { |
| if (state == UP) { |
| etype = NSLeftMouseUp; |
| + g_mouse_button_down[LEFT] = false; |
| } else { |
| etype = NSLeftMouseDown; |
| + g_mouse_button_down[LEFT] = true; |
| } |
| } else if (type == MIDDLE) { |
| if (state == UP) { |
| etype = NSOtherMouseUp; |
| + g_mouse_button_down[MIDDLE] = false; |
| } else { |
| etype = NSOtherMouseDown; |
| + g_mouse_button_down[MIDDLE] = true; |
| } |
| } else if (type == RIGHT) { |
| if (state == UP) { |
| etype = NSRightMouseUp; |
| + g_mouse_button_down[RIGHT] = false; |
| } else { |
| etype = NSRightMouseDown; |
| + g_mouse_button_down[RIGHT] = true; |
|
tapted
2016/03/01 08:11:58
these can all be collapsed into
g_mouse_button_do
themblsha
2016/03/09 17:40:22
Done.
|
| } |
| } else { |
| return false; |
| @@ -306,6 +418,11 @@ bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, |
| if (window) |
| pointInWindow = [window convertScreenToBase:pointInWindow]; |
| + ScopedNSEventSwizzler::Install(); |
| + |
| + // we want to destroy the autoreleased event ASAP |
| + base::mac::ScopedNSAutoreleasePool pool; |
| + |
| NSEvent* event = |
| [NSEvent mouseEventWithType:etype |
| location:pointInWindow |
| @@ -316,13 +433,16 @@ bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, |
| eventNumber:0 |
| clickCount:1 |
| pressure:(state == DOWN ? 1.0 : 0.0 )]; |
| - [[NSApplication sharedApplication] postEvent:event atStart:NO]; |
| if (!task.is_null()) { |
| - base::MessageLoop::current()->PostTask( |
| - FROM_HERE, base::Bind(&EventQueueWatcher, task)); |
| + base::scoped_nsobject<EventDeletedTaskRunner> task_runner = |
| + [[EventDeletedTaskRunner alloc] initWithClosure:task]; |
| + objc_setAssociatedObject(event, &kEventDeletedTaskRunnerKey, |
| + task_runner.get(), OBJC_ASSOCIATION_RETAIN); |
| } |
| + [[NSApplication sharedApplication] postEvent:event atStart:NO]; |
| + |
| return true; |
| } |