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; |
} |