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. | |
21 ui::test::EventGenerator* g_active_generator = NULL; | |
22 | |
23 // Set (and always cleared) in EmulateSendEvent() to provide an answer for | 20 // Set (and always cleared) in EmulateSendEvent() to provide an answer for |
24 // [NSApp currentEvent]. | 21 // [NSApp currentEvent]. |
25 NSEvent* g_current_event = nil; | 22 NSEvent* g_current_event = nil; |
26 | 23 |
27 } // namespace | 24 } // namespace |
28 | 25 |
29 @interface NSEventDonor : NSObject | 26 @interface NSEventDonor : NSObject |
30 @end | 27 @end |
31 | 28 |
32 @interface NSApplicationDonor : NSObject | 29 @interface NSApplicationDonor : NSObject |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
227 public ui::test::EventGeneratorDelegate { | 224 public ui::test::EventGeneratorDelegate { |
228 public: | 225 public: |
229 static EventGeneratorDelegateMac* GetInstance() { | 226 static EventGeneratorDelegateMac* GetInstance() { |
230 return Singleton<EventGeneratorDelegateMac>::get(); | 227 return Singleton<EventGeneratorDelegateMac>::get(); |
231 } | 228 } |
232 | 229 |
233 IMP CurrentEventMethod() { | 230 IMP CurrentEventMethod() { |
234 return swizzle_current_event_->GetOriginalImplementation(); | 231 return swizzle_current_event_->GetOriginalImplementation(); |
235 } | 232 } |
236 | 233 |
| 234 NSWindow* window() { return window_.get(); } |
| 235 ui::test::EventGenerator* owner() { return owner_; } |
| 236 |
237 // Overridden from ui::EventTarget: | 237 // Overridden from ui::EventTarget: |
238 bool CanAcceptEvent(const ui::Event& event) override { return true; } | 238 bool CanAcceptEvent(const ui::Event& event) override { return true; } |
239 ui::EventTarget* GetParentTarget() override { return NULL; } | 239 ui::EventTarget* GetParentTarget() override { return NULL; } |
240 scoped_ptr<ui::EventTargetIterator> GetChildIterator() const override; | 240 scoped_ptr<ui::EventTargetIterator> GetChildIterator() const override; |
241 ui::EventTargeter* GetEventTargeter() override { return this; } | 241 ui::EventTargeter* GetEventTargeter() override { return this; } |
242 | 242 |
243 // Overridden from ui::EventHandler (via ui::EventTarget): | 243 // Overridden from ui::EventHandler (via ui::EventTarget): |
244 void OnMouseEvent(ui::MouseEvent* event) override; | 244 void OnMouseEvent(ui::MouseEvent* event) override; |
245 void OnKeyEvent(ui::KeyEvent* event) override; | 245 void OnKeyEvent(ui::KeyEvent* event) override; |
246 void OnTouchEvent(ui::TouchEvent* event) override; | 246 void OnTouchEvent(ui::TouchEvent* event) override; |
(...skipping 29 matching lines...) Expand all Loading... |
276 | 276 |
277 private: | 277 private: |
278 friend struct DefaultSingletonTraits<EventGeneratorDelegateMac>; | 278 friend struct DefaultSingletonTraits<EventGeneratorDelegateMac>; |
279 | 279 |
280 EventGeneratorDelegateMac(); | 280 EventGeneratorDelegateMac(); |
281 ~EventGeneratorDelegateMac() override; | 281 ~EventGeneratorDelegateMac() override; |
282 | 282 |
283 ui::test::EventGenerator* owner_; | 283 ui::test::EventGenerator* owner_; |
284 base::scoped_nsobject<NSWindow> window_; | 284 base::scoped_nsobject<NSWindow> window_; |
285 scoped_ptr<base::mac::ScopedObjCClassSwizzler> swizzle_pressed_; | 285 scoped_ptr<base::mac::ScopedObjCClassSwizzler> swizzle_pressed_; |
| 286 scoped_ptr<base::mac::ScopedObjCClassSwizzler> swizzle_location_; |
286 scoped_ptr<base::mac::ScopedObjCClassSwizzler> swizzle_current_event_; | 287 scoped_ptr<base::mac::ScopedObjCClassSwizzler> swizzle_current_event_; |
287 base::scoped_nsobject<NSMenu> fake_menu_; | 288 base::scoped_nsobject<NSMenu> fake_menu_; |
288 | 289 |
289 DISALLOW_COPY_AND_ASSIGN(EventGeneratorDelegateMac); | 290 DISALLOW_COPY_AND_ASSIGN(EventGeneratorDelegateMac); |
290 }; | 291 }; |
291 | 292 |
292 EventGeneratorDelegateMac::EventGeneratorDelegateMac() : owner_(NULL) { | 293 EventGeneratorDelegateMac::EventGeneratorDelegateMac() : owner_(NULL) { |
293 DCHECK(!ui::test::EventGenerator::default_delegate); | 294 DCHECK(!ui::test::EventGenerator::default_delegate); |
294 ui::test::EventGenerator::default_delegate = this; | 295 ui::test::EventGenerator::default_delegate = this; |
295 // Install a fake "edit" menu. This is normally provided by Chrome's | 296 // Install a fake "edit" menu. This is normally provided by Chrome's |
(...skipping 24 matching lines...) Expand all Loading... |
320 ui::test::EventGenerator::default_delegate = NULL; | 321 ui::test::EventGenerator::default_delegate = NULL; |
321 } | 322 } |
322 | 323 |
323 scoped_ptr<ui::EventTargetIterator> | 324 scoped_ptr<ui::EventTargetIterator> |
324 EventGeneratorDelegateMac::GetChildIterator() const { | 325 EventGeneratorDelegateMac::GetChildIterator() const { |
325 // Return NULL to dispatch all events to the result of GetRootTarget(). | 326 // Return NULL to dispatch all events to the result of GetRootTarget(). |
326 return nullptr; | 327 return nullptr; |
327 } | 328 } |
328 | 329 |
329 void EventGeneratorDelegateMac::OnMouseEvent(ui::MouseEvent* event) { | 330 void EventGeneratorDelegateMac::OnMouseEvent(ui::MouseEvent* event) { |
330 // For mouse drag events, ensure the swizzled methods return the right flags. | |
331 base::AutoReset<ui::test::EventGenerator*> reset(&g_active_generator, owner_); | |
332 NSEvent* ns_event = CreateMouseEventInWindow(window_, | 331 NSEvent* ns_event = CreateMouseEventInWindow(window_, |
333 event->type(), | 332 event->type(), |
334 event->location(), | 333 event->location(), |
335 event->changed_button_flags()); | 334 event->changed_button_flags()); |
336 if (owner_->targeting_application()) | 335 if (owner_->targeting_application()) |
337 [NSApp sendEvent:ns_event]; | 336 [NSApp sendEvent:ns_event]; |
338 else | 337 else |
339 EmulateSendEvent(window_, ns_event); | 338 EmulateSendEvent(window_, ns_event); |
340 } | 339 } |
341 | 340 |
(...skipping 14 matching lines...) Expand all Loading... |
356 } | 355 } |
357 | 356 |
358 void EventGeneratorDelegateMac::OnTouchEvent(ui::TouchEvent* event) { | 357 void EventGeneratorDelegateMac::OnTouchEvent(ui::TouchEvent* event) { |
359 NOTREACHED() << "Touchscreen events not supported on Chrome Mac."; | 358 NOTREACHED() << "Touchscreen events not supported on Chrome Mac."; |
360 } | 359 } |
361 | 360 |
362 void EventGeneratorDelegateMac::SetContext(ui::test::EventGenerator* owner, | 361 void EventGeneratorDelegateMac::SetContext(ui::test::EventGenerator* owner, |
363 gfx::NativeWindow root_window, | 362 gfx::NativeWindow root_window, |
364 gfx::NativeWindow window) { | 363 gfx::NativeWindow window) { |
365 swizzle_pressed_.reset(); | 364 swizzle_pressed_.reset(); |
| 365 swizzle_location_.reset(); |
366 swizzle_current_event_.reset(); | 366 swizzle_current_event_.reset(); |
367 owner_ = owner; | 367 owner_ = owner; |
368 | 368 |
369 // Retain the NSWindow (note it can be nil). This matches Cocoa's tendency to | 369 // Retain the NSWindow (note it can be nil). This matches Cocoa's tendency to |
370 // have autoreleased objects, or objects still in the event queue, that | 370 // have autoreleased objects, or objects still in the event queue, that |
371 // reference the NSWindow. | 371 // reference the NSWindow. |
372 window_.reset([window retain]); | 372 window_.reset([window retain]); |
373 | 373 |
374 // Normally, edit menu items have a `nil` target. This results in -[NSMenu | 374 // Normally, edit menu items have a `nil` target. This results in -[NSMenu |
375 // performKeyEquivalent:] relying on -[NSApplication targetForAction:to:from:] | 375 // performKeyEquivalent:] relying on -[NSApplication targetForAction:to:from:] |
376 // to find a target starting at the first responder of the key window. Since | 376 // 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 | 377 // non-interactive tests have no key window, that won't work. So set (or |
378 // clear) the target explicitly on all menu items. | 378 // clear) the target explicitly on all menu items. |
379 [[fake_menu_ itemArray] makeObjectsPerformSelector:@selector(setTarget:) | 379 [[fake_menu_ itemArray] makeObjectsPerformSelector:@selector(setTarget:) |
380 withObject:[window firstResponder]]; | 380 withObject:[window firstResponder]]; |
381 | 381 |
382 if (owner_) { | 382 if (owner_) { |
383 swizzle_pressed_.reset(new base::mac::ScopedObjCClassSwizzler( | 383 swizzle_pressed_.reset(new base::mac::ScopedObjCClassSwizzler( |
384 [NSEvent class], | 384 [NSEvent class], |
385 [NSEventDonor class], | 385 [NSEventDonor class], |
386 @selector(pressedMouseButtons))); | 386 @selector(pressedMouseButtons))); |
| 387 swizzle_location_.reset(new base::mac::ScopedObjCClassSwizzler( |
| 388 [NSEvent class], [NSEventDonor class], @selector(mouseLocation))); |
387 swizzle_current_event_.reset(new base::mac::ScopedObjCClassSwizzler( | 389 swizzle_current_event_.reset(new base::mac::ScopedObjCClassSwizzler( |
388 [NSApplication class], | 390 [NSApplication class], |
389 [NSApplicationDonor class], | 391 [NSApplicationDonor class], |
390 @selector(currentEvent))); | 392 @selector(currentEvent))); |
391 } | 393 } |
392 } | 394 } |
393 | 395 |
394 gfx::Point EventGeneratorDelegateMac::CenterOfTarget( | 396 gfx::Point EventGeneratorDelegateMac::CenterOfTarget( |
395 const ui::EventTarget* target) const { | 397 const ui::EventTarget* target) const { |
396 DCHECK_EQ(target, this); | 398 DCHECK_EQ(target, this); |
397 return CenterOfWindow(window_); | 399 return CenterOfWindow(window_); |
398 } | 400 } |
399 | 401 |
400 gfx::Point EventGeneratorDelegateMac::CenterOfWindow( | 402 gfx::Point EventGeneratorDelegateMac::CenterOfWindow( |
401 gfx::NativeWindow window) const { | 403 gfx::NativeWindow window) const { |
402 DCHECK_EQ(window, window_); | 404 DCHECK_EQ(window, window_); |
403 return gfx::ScreenRectFromNSRect([window frame]).CenterPoint(); | 405 return gfx::ScreenRectFromNSRect([window frame]).CenterPoint(); |
404 } | 406 } |
405 | 407 |
| 408 // Return the current owner of the EventGeneratorDelegate. May be null. |
| 409 ui::test::EventGenerator* GetActiveGenerator() { |
| 410 return EventGeneratorDelegateMac::GetInstance()->owner(); |
| 411 } |
| 412 |
406 } // namespace | 413 } // namespace |
407 | 414 |
408 namespace views { | 415 namespace views { |
409 namespace test { | 416 namespace test { |
410 | 417 |
411 void InitializeMacEventGeneratorDelegate() { | 418 void InitializeMacEventGeneratorDelegate() { |
412 EventGeneratorDelegateMac::GetInstance(); | 419 EventGeneratorDelegateMac::GetInstance(); |
413 } | 420 } |
414 | 421 |
415 } // namespace test | 422 } // namespace test |
416 } // namespace views | 423 } // namespace views |
417 | 424 |
418 @implementation NSEventDonor | 425 @implementation NSEventDonor |
419 | 426 |
420 // Donate +[NSEvent pressedMouseButtons] by retrieving the flags from the | 427 // Donate +[NSEvent pressedMouseButtons] by retrieving the flags from the |
421 // active generator. | 428 // active generator. |
422 + (NSUInteger)pressedMouseButtons { | 429 + (NSUInteger)pressedMouseButtons { |
423 if (!g_active_generator) | 430 ui::test::EventGenerator* generator = GetActiveGenerator(); |
| 431 if (!generator) |
424 return [NSEventDonor pressedMouseButtons]; // Call original implementation. | 432 return [NSEventDonor pressedMouseButtons]; // Call original implementation. |
425 | 433 |
426 int flags = g_active_generator->flags(); | 434 int flags = generator->flags(); |
427 NSUInteger bitmask = 0; | 435 NSUInteger bitmask = 0; |
428 if (flags & ui::EF_LEFT_MOUSE_BUTTON) | 436 if (flags & ui::EF_LEFT_MOUSE_BUTTON) |
429 bitmask |= 1; | 437 bitmask |= 1; |
430 if (flags & ui::EF_RIGHT_MOUSE_BUTTON) | 438 if (flags & ui::EF_RIGHT_MOUSE_BUTTON) |
431 bitmask |= 1 << 1; | 439 bitmask |= 1 << 1; |
432 if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) | 440 if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) |
433 bitmask |= 1 << 2; | 441 bitmask |= 1 << 2; |
434 return bitmask; | 442 return bitmask; |
435 } | 443 } |
436 | 444 |
| 445 // Donate +[NSEvent mouseLocation] by retrieving the current position on screen. |
| 446 + (NSPoint)mouseLocation { |
| 447 ui::test::EventGenerator* generator = GetActiveGenerator(); |
| 448 if (!generator) |
| 449 return [NSEventDonor mouseLocation]; // Call original implementation. |
| 450 |
| 451 // The location is the point in the root window which, for desktop widgets, is |
| 452 // the widget itself. |
| 453 gfx::Point point_in_root = generator->current_location(); |
| 454 NSWindow* window = EventGeneratorDelegateMac::GetInstance()->window(); |
| 455 NSPoint point_in_window = ConvertRootPointToTarget(window, point_in_root); |
| 456 return [window convertBaseToScreen:point_in_window]; |
| 457 } |
| 458 |
437 @end | 459 @end |
438 | 460 |
439 @implementation NSApplicationDonor | 461 @implementation NSApplicationDonor |
440 | 462 |
441 - (NSEvent*)currentEvent { | 463 - (NSEvent*)currentEvent { |
442 if (g_current_event) | 464 if (g_current_event) |
443 return g_current_event; | 465 return g_current_event; |
444 | 466 |
445 // Find the original implementation and invoke it. | 467 // Find the original implementation and invoke it. |
446 IMP original = EventGeneratorDelegateMac::GetInstance()->CurrentEventMethod(); | 468 IMP original = EventGeneratorDelegateMac::GetInstance()->CurrentEventMethod(); |
447 return original(self, _cmd); | 469 return original(self, _cmd); |
448 } | 470 } |
449 | 471 |
450 @end | 472 @end |
OLD | NEW |