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