Index: ui/events/test/cocoa_test_event_utils.mm |
diff --git a/ui/events/test/cocoa_test_event_utils.mm b/ui/events/test/cocoa_test_event_utils.mm |
index 490382553f4081b03a193865e0efc180b2ac9774..b225710adc61099936f04e5b7c03d0714c0481cb 100644 |
--- a/ui/events/test/cocoa_test_event_utils.mm |
+++ b/ui/events/test/cocoa_test_event_utils.mm |
@@ -135,18 +135,79 @@ std::pair<NSEvent*, NSEvent*> RightMouseClickInView(NSView* view, |
return std::make_pair(down, up); |
} |
-NSEvent* TestScrollEvent(NSPoint location, |
+NSEvent* TestScrollEvent(NSPoint window_point, |
NSWindow* window, |
CGFloat delta_x, |
- CGFloat delta_y) { |
+ CGFloat delta_y, |
+ bool has_precise_deltas, |
+ NSEventPhase event_phase, |
+ NSEventPhase momentum_phase) { |
const uint32_t wheel_count = 2; |
int32_t wheel1 = static_cast<int>(delta_y); |
int32_t wheel2 = static_cast<int>(delta_x); |
- CGScrollEventUnit units = kCGScrollEventUnitLine; |
+ CGScrollEventUnit units = |
+ has_precise_deltas ? kCGScrollEventUnitPixel : kCGScrollEventUnitLine; |
base::ScopedCFTypeRef<CGEventRef> scroll(CGEventCreateScrollWheelEvent( |
nullptr, units, wheel_count, wheel1, wheel2)); |
- CGEventSetLocation(scroll, ScreenPointFromWindow(location, window)); |
- return AttachWindowToCGEvent(scroll, window); |
+ CGEventSetLocation(scroll, ScreenPointFromWindow(window_point, window)); |
+ |
+ // Always set event flags, otherwise +[NSEvent eventWithCGEvent:] populates |
+ // flags from current keyboard state which can make tests flaky. |
+ CGEventSetFlags(scroll, 0); |
+ |
+ if (has_precise_deltas) { |
+ // kCGScrollWheelEventIsContinuous is -[NSEvent hasPreciseScrollingDeltas]. |
+ // CGEventTypes.h says it should be non-zero for pixel-based scrolling. |
+ // Verify that CGEventCreateScrollWheelEvent() set it. |
+ DCHECK_EQ(1, CGEventGetIntegerValueField(scroll, |
+ kCGScrollWheelEventIsContinuous)); |
+ } |
+ |
+ // Don't set phase information when neither. |
+ if (event_phase != NSEventPhaseNone || momentum_phase != NSEventPhaseNone) { |
+ // AppKit conflates CGScrollPhase (bitmask flags) and CGMomentumScrollPhase |
+ // (an enum) into NSEventPhase, where it is used for both -[NSEvent phase] |
+ // and -[NSEvent momentumPhase]. Do a reverse mapping here. |
+ int cg_event_phase = 0; |
+ if (event_phase & NSEventPhaseBegan) |
+ cg_event_phase |= kCGScrollPhaseBegan; |
+ if (event_phase & NSEventPhaseChanged) |
+ cg_event_phase |= kCGScrollPhaseChanged; |
+ if (event_phase & NSEventPhaseEnded) |
+ cg_event_phase |= kCGScrollPhaseEnded; |
+ if (event_phase & NSEventPhaseCancelled) |
+ cg_event_phase |= kCGScrollPhaseCancelled; |
+ if (event_phase & NSEventPhaseMayBegin) |
+ cg_event_phase |= kCGScrollPhaseMayBegin; |
+ |
+ CGMomentumScrollPhase cg_momentum_phase = kCGMomentumScrollPhaseNone; |
+ switch (momentum_phase) { |
+ case NSEventPhaseNone: |
+ break; |
+ case NSEventPhaseBegan: |
+ cg_momentum_phase = kCGMomentumScrollPhaseBegin; |
+ break; |
+ case NSEventPhaseChanged: |
+ cg_momentum_phase = kCGMomentumScrollPhaseContinue; |
+ break; |
+ case NSEventPhaseEnded: |
+ cg_momentum_phase = kCGMomentumScrollPhaseEnd; |
+ break; |
+ default: |
+ // Those are the only 4 options for CGMomentumScrollPhase. If something |
+ // else was provided it should probably never appear on an NSEvent. |
+ NOTREACHED(); |
+ } |
+ CGEventSetIntegerValueField(scroll, kCGScrollWheelEventScrollPhase, |
+ cg_event_phase); |
+ CGEventSetIntegerValueField(scroll, kCGScrollWheelEventMomentumPhase, |
+ cg_momentum_phase); |
+ } |
+ NSEvent* event = AttachWindowToCGEvent(scroll, window); |
+ DCHECK_EQ(has_precise_deltas, [event hasPreciseScrollingDeltas]); |
+ DCHECK_EQ(event_phase, [event phase]); |
+ DCHECK_EQ(momentum_phase, [event momentumPhase]); |
+ return event; |
} |
NSEvent* KeyEventWithCharacter(unichar c) { |