OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 #import <Cocoa/Cocoa.h> | 5 #import <Cocoa/Cocoa.h> |
6 | 6 |
7 #import "base/mac/scoped_nsobject.h" | 7 #import "base/mac/scoped_nsobject.h" |
8 #import "base/mac/scoped_objc_class_swizzler.h" | 8 #import "base/mac/scoped_objc_class_swizzler.h" |
9 #include "base/memory/singleton.h" | 9 #include "base/memory/singleton.h" |
10 #include "ui/events/event_processor.h" | 10 #include "ui/events/event_processor.h" |
11 #include "ui/events/event_target.h" | 11 #include "ui/events/event_target.h" |
12 #include "ui/events/event_target_iterator.h" | 12 #include "ui/events/event_target_iterator.h" |
13 #include "ui/events/event_targeter.h" | 13 #include "ui/events/event_targeter.h" |
14 #import "ui/events/test/cocoa_test_event_utils.h" | 14 #import "ui/events/test/cocoa_test_event_utils.h" |
15 #include "ui/events/test/event_generator.h" | 15 #include "ui/events/test/event_generator.h" |
16 #include "ui/gfx/mac/coordinate_conversion.h" | 16 #include "ui/gfx/mac/coordinate_conversion.h" |
17 | 17 |
18 namespace { | 18 namespace { |
19 | 19 |
20 // Singleton to provide state for swizzled Objective C methods. | 20 // Singleton to provide state for swizzled Objective C methods. |
21 ui::test::EventGenerator* g_active_generator = NULL; | 21 ui::test::EventGenerator* g_active_generator = NULL; |
22 | 22 |
| 23 // Set (and always cleared) in EmulateSendEvent() to provide an answer for |
| 24 // [NSApp currentEvent]. |
| 25 NSEvent* g_current_event = nil; |
| 26 |
23 } // namespace | 27 } // namespace |
24 | 28 |
25 @interface NSEventDonor : NSObject | 29 @interface NSEventDonor : NSObject |
26 @end | 30 @end |
27 | 31 |
28 @implementation NSEventDonor | 32 @interface NSApplicationDonor : NSObject |
29 | |
30 // Donate +[NSEvent pressedMouseButtons] by retrieving the flags from the | |
31 // active generator. | |
32 + (NSUInteger)pressedMouseButtons { | |
33 if (!g_active_generator) | |
34 return [NSEventDonor pressedMouseButtons]; // Call original implementation. | |
35 | |
36 int flags = g_active_generator->flags(); | |
37 NSUInteger bitmask = 0; | |
38 if (flags & ui::EF_LEFT_MOUSE_BUTTON) | |
39 bitmask |= 1; | |
40 if (flags & ui::EF_RIGHT_MOUSE_BUTTON) | |
41 bitmask |= 1 << 1; | |
42 if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) | |
43 bitmask |= 1 << 2; | |
44 return bitmask; | |
45 } | |
46 | |
47 @end | 33 @end |
48 | 34 |
49 namespace { | 35 namespace { |
50 | 36 |
51 NSPoint ConvertRootPointToTarget(NSWindow* target, | 37 NSPoint ConvertRootPointToTarget(NSWindow* target, |
52 const gfx::Point& point_in_root) { | 38 const gfx::Point& point_in_root) { |
53 // Normally this would do [NSWindow convertScreenToBase:]. However, Cocoa can | 39 // Normally this would do [NSWindow convertScreenToBase:]. However, Cocoa can |
54 // reposition the window on screen and make things flaky. Initially, just | 40 // reposition the window on screen and make things flaky. Initially, just |
55 // assume that the contentRect of |target| is at the top-left corner of the | 41 // assume that the contentRect of |target| is at the top-left corner of the |
56 // screen. | 42 // screen. |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 default: | 112 default: |
127 NOTREACHED(); | 113 NOTREACHED(); |
128 return 0; | 114 return 0; |
129 } | 115 } |
130 } | 116 } |
131 | 117 |
132 // Emulate the dispatching that would be performed by -[NSWindow sendEvent:]. | 118 // Emulate the dispatching that would be performed by -[NSWindow sendEvent:]. |
133 // sendEvent is a black box which (among other things) will try to peek at the | 119 // sendEvent is a black box which (among other things) will try to peek at the |
134 // event queue and can block indefinitely. | 120 // event queue and can block indefinitely. |
135 void EmulateSendEvent(NSWindow* window, NSEvent* event) { | 121 void EmulateSendEvent(NSWindow* window, NSEvent* event) { |
| 122 base::AutoReset<NSEvent*> reset(&g_current_event, event); |
136 NSResponder* responder = [window firstResponder]; | 123 NSResponder* responder = [window firstResponder]; |
137 switch ([event type]) { | 124 switch ([event type]) { |
138 case NSKeyDown: | 125 case NSKeyDown: |
139 [responder keyDown:event]; | 126 [responder keyDown:event]; |
140 return; | 127 return; |
141 case NSKeyUp: | 128 case NSKeyUp: |
142 [responder keyUp:event]; | 129 [responder keyUp:event]; |
143 return; | 130 return; |
144 } | 131 } |
145 | 132 |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 class EventGeneratorDelegateMac : public ui::EventTarget, | 223 class EventGeneratorDelegateMac : public ui::EventTarget, |
237 public ui::EventSource, | 224 public ui::EventSource, |
238 public ui::EventProcessor, | 225 public ui::EventProcessor, |
239 public ui::EventTargeter, | 226 public ui::EventTargeter, |
240 public ui::test::EventGeneratorDelegate { | 227 public ui::test::EventGeneratorDelegate { |
241 public: | 228 public: |
242 static EventGeneratorDelegateMac* GetInstance() { | 229 static EventGeneratorDelegateMac* GetInstance() { |
243 return Singleton<EventGeneratorDelegateMac>::get(); | 230 return Singleton<EventGeneratorDelegateMac>::get(); |
244 } | 231 } |
245 | 232 |
| 233 IMP CurrentEventMethod() { |
| 234 return swizzle_current_event_->GetOriginalImplementation(); |
| 235 } |
| 236 |
246 // Overridden from ui::EventTarget: | 237 // Overridden from ui::EventTarget: |
247 bool CanAcceptEvent(const ui::Event& event) override { return true; } | 238 bool CanAcceptEvent(const ui::Event& event) override { return true; } |
248 ui::EventTarget* GetParentTarget() override { return NULL; } | 239 ui::EventTarget* GetParentTarget() override { return NULL; } |
249 scoped_ptr<ui::EventTargetIterator> GetChildIterator() const override; | 240 scoped_ptr<ui::EventTargetIterator> GetChildIterator() const override; |
250 ui::EventTargeter* GetEventTargeter() override { return this; } | 241 ui::EventTargeter* GetEventTargeter() override { return this; } |
251 | 242 |
252 // Overridden from ui::EventHandler (via ui::EventTarget): | 243 // Overridden from ui::EventHandler (via ui::EventTarget): |
253 void OnMouseEvent(ui::MouseEvent* event) override; | 244 void OnMouseEvent(ui::MouseEvent* event) override; |
254 void OnKeyEvent(ui::KeyEvent* event) override; | 245 void OnKeyEvent(ui::KeyEvent* event) override; |
255 | 246 |
(...skipping 28 matching lines...) Expand all Loading... |
284 | 275 |
285 private: | 276 private: |
286 friend struct DefaultSingletonTraits<EventGeneratorDelegateMac>; | 277 friend struct DefaultSingletonTraits<EventGeneratorDelegateMac>; |
287 | 278 |
288 EventGeneratorDelegateMac(); | 279 EventGeneratorDelegateMac(); |
289 ~EventGeneratorDelegateMac() override; | 280 ~EventGeneratorDelegateMac() override; |
290 | 281 |
291 ui::test::EventGenerator* owner_; | 282 ui::test::EventGenerator* owner_; |
292 NSWindow* window_; | 283 NSWindow* window_; |
293 scoped_ptr<base::mac::ScopedObjCClassSwizzler> swizzle_pressed_; | 284 scoped_ptr<base::mac::ScopedObjCClassSwizzler> swizzle_pressed_; |
| 285 scoped_ptr<base::mac::ScopedObjCClassSwizzler> swizzle_current_event_; |
294 base::scoped_nsobject<NSMenu> fake_menu_; | 286 base::scoped_nsobject<NSMenu> fake_menu_; |
295 | 287 |
296 DISALLOW_COPY_AND_ASSIGN(EventGeneratorDelegateMac); | 288 DISALLOW_COPY_AND_ASSIGN(EventGeneratorDelegateMac); |
297 }; | 289 }; |
298 | 290 |
299 EventGeneratorDelegateMac::EventGeneratorDelegateMac() | 291 EventGeneratorDelegateMac::EventGeneratorDelegateMac() |
300 : owner_(NULL), | 292 : owner_(NULL), |
301 window_(NULL) { | 293 window_(NULL) { |
302 DCHECK(!ui::test::EventGenerator::default_delegate); | 294 DCHECK(!ui::test::EventGenerator::default_delegate); |
303 ui::test::EventGenerator::default_delegate = this; | 295 ui::test::EventGenerator::default_delegate = this; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
361 if ([fake_menu_ performKeyEquivalent:ns_event]) | 353 if ([fake_menu_ performKeyEquivalent:ns_event]) |
362 return; | 354 return; |
363 | 355 |
364 EmulateSendEvent(window_, ns_event); | 356 EmulateSendEvent(window_, ns_event); |
365 } | 357 } |
366 | 358 |
367 void EventGeneratorDelegateMac::SetContext(ui::test::EventGenerator* owner, | 359 void EventGeneratorDelegateMac::SetContext(ui::test::EventGenerator* owner, |
368 gfx::NativeWindow root_window, | 360 gfx::NativeWindow root_window, |
369 gfx::NativeWindow window) { | 361 gfx::NativeWindow window) { |
370 swizzle_pressed_.reset(); | 362 swizzle_pressed_.reset(); |
| 363 swizzle_current_event_.reset(); |
371 owner_ = owner; | 364 owner_ = owner; |
372 window_ = window; | 365 window_ = window; |
373 | 366 |
374 // Normally, edit menu items have a `nil` target. This results in -[NSMenu | 367 // Normally, edit menu items have a `nil` target. This results in -[NSMenu |
375 // performKeyEquivalent:] relying on -[NSApplication targetForAction:to:from:] | 368 // performKeyEquivalent:] relying on -[NSApplication targetForAction:to:from:] |
376 // to find a target starting at the first responder of the key window. Since | 369 // to find a target starting at the first responder of the key window. Since |
377 // non-interactive tests have no key window, that won't work. So set (or | 370 // non-interactive tests have no key window, that won't work. So set (or |
378 // clear) the target explicitly on all menu items. | 371 // clear) the target explicitly on all menu items. |
379 [[fake_menu_ itemArray] makeObjectsPerformSelector:@selector(setTarget:) | 372 [[fake_menu_ itemArray] makeObjectsPerformSelector:@selector(setTarget:) |
380 withObject:[window firstResponder]]; | 373 withObject:[window firstResponder]]; |
381 | 374 |
382 if (owner_) { | 375 if (owner_) { |
383 swizzle_pressed_.reset(new base::mac::ScopedObjCClassSwizzler( | 376 swizzle_pressed_.reset(new base::mac::ScopedObjCClassSwizzler( |
384 [NSEvent class], | 377 [NSEvent class], |
385 [NSEventDonor class], | 378 [NSEventDonor class], |
386 @selector(pressedMouseButtons))); | 379 @selector(pressedMouseButtons))); |
| 380 swizzle_current_event_.reset(new base::mac::ScopedObjCClassSwizzler( |
| 381 [NSApplication class], |
| 382 [NSApplicationDonor class], |
| 383 @selector(currentEvent))); |
387 } | 384 } |
388 } | 385 } |
389 | 386 |
390 gfx::Point EventGeneratorDelegateMac::CenterOfTarget( | 387 gfx::Point EventGeneratorDelegateMac::CenterOfTarget( |
391 const ui::EventTarget* target) const { | 388 const ui::EventTarget* target) const { |
392 DCHECK_EQ(target, this); | 389 DCHECK_EQ(target, this); |
393 return CenterOfWindow(window_); | 390 return CenterOfWindow(window_); |
394 } | 391 } |
395 | 392 |
396 gfx::Point EventGeneratorDelegateMac::CenterOfWindow( | 393 gfx::Point EventGeneratorDelegateMac::CenterOfWindow( |
397 gfx::NativeWindow window) const { | 394 gfx::NativeWindow window) const { |
398 DCHECK_EQ(window, window_); | 395 DCHECK_EQ(window, window_); |
399 return gfx::ScreenRectFromNSRect([window frame]).CenterPoint(); | 396 return gfx::ScreenRectFromNSRect([window frame]).CenterPoint(); |
400 } | 397 } |
401 | 398 |
402 } // namespace | 399 } // namespace |
403 | 400 |
404 namespace views { | 401 namespace views { |
405 namespace test { | 402 namespace test { |
406 | 403 |
407 void InitializeMacEventGeneratorDelegate() { | 404 void InitializeMacEventGeneratorDelegate() { |
408 EventGeneratorDelegateMac::GetInstance(); | 405 EventGeneratorDelegateMac::GetInstance(); |
409 } | 406 } |
410 | 407 |
411 } // namespace test | 408 } // namespace test |
412 } // namespace views | 409 } // namespace views |
| 410 |
| 411 @implementation NSEventDonor |
| 412 |
| 413 // Donate +[NSEvent pressedMouseButtons] by retrieving the flags from the |
| 414 // active generator. |
| 415 + (NSUInteger)pressedMouseButtons { |
| 416 if (!g_active_generator) |
| 417 return [NSEventDonor pressedMouseButtons]; // Call original implementation. |
| 418 |
| 419 int flags = g_active_generator->flags(); |
| 420 NSUInteger bitmask = 0; |
| 421 if (flags & ui::EF_LEFT_MOUSE_BUTTON) |
| 422 bitmask |= 1; |
| 423 if (flags & ui::EF_RIGHT_MOUSE_BUTTON) |
| 424 bitmask |= 1 << 1; |
| 425 if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) |
| 426 bitmask |= 1 << 2; |
| 427 return bitmask; |
| 428 } |
| 429 |
| 430 @end |
| 431 |
| 432 @implementation NSApplicationDonor |
| 433 |
| 434 - (NSEvent*)currentEvent { |
| 435 if (g_current_event) |
| 436 return g_current_event; |
| 437 |
| 438 // Find the original implementation and invoke it. |
| 439 IMP original = EventGeneratorDelegateMac::GetInstance()->CurrentEventMethod(); |
| 440 return original(self, _cmd); |
| 441 } |
| 442 |
| 443 @end |
OLD | NEW |