diff options
3 files changed, 68 insertions, 4 deletions
diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h index 2fadf10..c2383f8 100644 --- a/base/mac/sdk_forward_declarations.h +++ b/base/mac/sdk_forward_declarations.h @@ -43,6 +43,7 @@ typedef NSInteger NSWindowAnimationBehavior; @interface NSEvent (LionSDK) + (BOOL)isSwipeTrackingFromScrollEventsEnabled; +- (NSEventPhase)momentumPhase; - (NSEventPhase)phase; - (CGFloat)scrollingDeltaX; - (CGFloat)scrollingDeltaY; diff --git a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm index 306ff08..08c758e 100644 --- a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm +++ b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm @@ -225,8 +225,10 @@ - (void)beginHistorySwipeInDirection: (history_swiper::NavigationDirection)direction event:(NSEvent*)event { - // There shouldn't be an existing history overlay. - DCHECK(historyOverlay_ == nil); + // We cannot make any assumptions about the current state of the + // historyOverlay_, since users may attempt to use multiple gesture input + // devices simultaneously, which confuses Cocoa. + [self endHistorySwipe]; HistoryOverlayController* historyOverlay = [[HistoryOverlayController alloc] initForMode:(direction == history_swiper::kForwards) @@ -302,7 +304,15 @@ if (![theEvent respondsToSelector:@selector(phase)]) return NO; - if ([theEvent phase] != NSEventPhaseChanged) + // If the window has a horizontal scroll bar, sometimes Cocoa gets confused + // and sends us momentum scroll wheel events instead of gesture scroll events + // (even though the user is still actively swiping). + if ([theEvent phase] != NSEventPhaseChanged && + [theEvent momentumPhase] != NSEventPhaseChanged) { + return NO; + } + + if (!inGesture_) return NO; CGFloat yDelta = gestureCurrentPoint_.y - gestureStartPoint_.y; diff --git a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm index 9765041..bd2d382 100644 --- a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm +++ b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm @@ -48,11 +48,15 @@ class MacHistorySwiperTest : public CocoaTest { event:[OCMArg any]]; [[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { ++begin_count_; + // beginHistorySwipeInDirection: calls endHistorySwipe internally. + --end_count_; }] andForwardToRealObject] beginHistorySwipeInDirection:history_swiper::kForwards event:[OCMArg any]]; [[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { ++begin_count_; + // beginHistorySwipeInDirection: calls endHistorySwipe internally. + --end_count_; }] andForwardToRealObject] beginHistorySwipeInDirection:history_swiper::kBackwards event:[OCMArg any]]; @@ -82,6 +86,7 @@ class MacHistorySwiperTest : public CocoaTest { void startGestureInMiddle(); void moveGestureInMiddle(); void moveGestureAtPoint(NSPoint point); + void momentumMoveGestureAtPoint(NSPoint point); void endGestureAtPoint(NSPoint point); HistorySwiper* historySwiper_; @@ -112,13 +117,19 @@ id mockEventWithPoint(NSPoint point, NSEventType type) { return mockEvent; } -id scrollWheelEventWithPhase(NSEventPhase phase) { +id scrollWheelEventWithPhase(NSEventPhase phase, NSEventPhase momentumPhase) { // The point isn't used, so we pass in bogus data. id event = mockEventWithPoint(makePoint(0,0), NSScrollWheel); [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(phase)] phase]; + [(NSEvent*) + [[event stub] andReturnValue:OCMOCK_VALUE(momentumPhase)] momentumPhase]; return event; } +id scrollWheelEventWithPhase(NSEventPhase phase) { + return scrollWheelEventWithPhase(phase, NSEventPhaseNone); +} + void MacHistorySwiperTest::startGestureInMiddle() { NSEvent* event = mockEventWithPoint(makePoint(0.5, 0.5), NSEventTypeGesture); [historySwiper_ touchesBeganWithEvent:event]; @@ -144,6 +155,15 @@ void MacHistorySwiperTest::moveGestureAtPoint(NSPoint point) { [historySwiper_ handleEvent:scrollEvent]; } +void MacHistorySwiperTest::momentumMoveGestureAtPoint(NSPoint point) { + NSEvent* event = mockEventWithPoint(point, NSEventTypeGesture); + [historySwiper_ touchesMovedWithEvent:event]; + + NSEvent* scrollEvent = + scrollWheelEventWithPhase(NSEventPhaseNone, NSEventPhaseChanged); + [historySwiper_ handleEvent:scrollEvent]; +} + void MacHistorySwiperTest::endGestureAtPoint(NSPoint point) { NSEvent* event = mockEventWithPoint(point, NSEventTypeGesture); [historySwiper_ touchesEndedWithEvent:event]; @@ -260,6 +280,39 @@ TEST_F(MacHistorySwiperTest, SwipeLeftThenDown) { EXPECT_FALSE(navigated_left_); } +// Sometimes Cocoa gets confused and sends us a momentum swipe event instead of +// a swipe gesture event. We should still function appropriately. +TEST_F(MacHistorySwiperTest, MomentumSwipeLeft) { + // These tests require 10.7+ APIs. + if (![NSEvent + respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) + return; + + startGestureInMiddle(); + + // Send a momentum move gesture. + momentumMoveGestureAtPoint(makePoint(0.5, 0.5)); + EXPECT_EQ(begin_count_, 0); + EXPECT_EQ(end_count_, 0); + + // Callbacks from blink to set the relevant state for history swiping. + [historySwiper_ gotUnhandledWheelEvent]; + [historySwiper_ scrollOffsetPinnedToLeft:YES toRight:YES]; + [historySwiper_ setHasHorizontalScrollbar:NO]; + + momentumMoveGestureAtPoint(makePoint(0.2, 0.5)); + EXPECT_EQ(begin_count_, 1); + EXPECT_EQ(end_count_, 0); + EXPECT_FALSE(navigated_right_); + EXPECT_FALSE(navigated_left_); + + endGestureAtPoint(makePoint(0.2, 0.5)); + EXPECT_EQ(begin_count_, 1); + EXPECT_EQ(end_count_, 1); + EXPECT_FALSE(navigated_right_); + EXPECT_TRUE(navigated_left_); +} + // User starts a swipe but doesn't move. TEST_F(MacHistorySwiperTest, NoSwipe) { // These tests require 10.7+ APIs. |