Commit bedaa557 authored by erikchen's avatar erikchen Committed by Commit bot

Mac: Don't allow history swiping after substantial vertical motion.

Previously, the logic for history swiping depended on the relative Y distance
of the gesture's current location from the gesture's start location. Gestures
which consisted of a large up motion, followed by a large down motion, followed
by a horizontal motion could still cause history swiping. The new logic depends
on the total Y distance of the gesture, which fixes this problem.

This CL also includes a change to allowed the gesture recognizer state machine
to enter the state kCancelled directly from the state kPending. Previously, it
was possible for the history overlay to show for a very brief period of time
even if the gesture was about to be cancelled.

This CL also includes a minor refactor:
 - Renamed several methods to better reflect their intended purpose.
 - Removed lastProcessedGestureId_ and currentGestureId_, which did not cause a
 functional change.

BUG=421629

Review URL: https://codereview.chromium.org/657323003

Cr-Commit-Position: refs/heads/master@{#300199}
parent 7387ae22
...@@ -13,10 +13,10 @@ class WebMouseWheelEvent; ...@@ -13,10 +13,10 @@ class WebMouseWheelEvent;
@class HistorySwiper; @class HistorySwiper;
@protocol HistorySwiperDelegate @protocol HistorySwiperDelegate
// Return NO from this method is the view/render_widget_host should not // Return NO from this method if the view/render_widget_host should not
// allow history swiping. // allow history swiping.
- (BOOL)shouldAllowHistorySwiping; - (BOOL)shouldAllowHistorySwiping;
// The history overlay is added to the view returning from this method. // The history overlay is added to the view returned from this method.
- (NSView*)viewThatWantsHistoryOverlay; - (NSView*)viewThatWantsHistoryOverlay;
@end @end
...@@ -25,6 +25,11 @@ enum NavigationDirection { ...@@ -25,6 +25,11 @@ enum NavigationDirection {
kBackwards = 0, kBackwards = 0,
kForwards, kForwards,
}; };
// The states of a state machine that recognizes the history swipe gesture.
// When a gesture first begins, the state is reset to kPending. The state
// machine only applies to trackpad gestures. Magic Mouse gestures use a
// different mechanism.
enum RecognitionState { enum RecognitionState {
// Waiting to see whether the renderer will handle the event with phase // Waiting to see whether the renderer will handle the event with phase
// NSEventPhaseBegan. The state machine will also stay in this state if // NSEventPhaseBegan. The state machine will also stay in this state if
...@@ -97,13 +102,13 @@ enum RecognitionState { ...@@ -97,13 +102,13 @@ enum RecognitionState {
// then this class will transition to the state kTracking. Events are // then this class will transition to the state kTracking. Events are
// consumed, and not passed to the renderer. // consumed, and not passed to the renderer.
// //
// There are multiple APIs that provide information about gestures on the // There are multiple callbacks that provide information about gestures on the
// trackpad. This class uses two different set of APIs. // trackpad. This class uses two different sets of callbacks.
// 1. The -[NSView touches*WithEvent:] APIs provide detailed information // 1. The -[NSView touches*WithEvent:] callbacks provide detailed information
// about the touches within a gesture. The callbacks happen with more // about the touches within a gesture. The callbacks happen with more
// frequency, and have higher accuracy. These APIs are used to transition // frequency, and have higher accuracy. These callbacks are used to
// between all state, exception for kPending -> kPotential. // transition between all states except for kPending -> kPotential.
// 2. The -[NSView scrollWheel:] API provides less information, but the // 2. The -[NSView scrollWheel:] callback provides less information, but the
// events are passed to the renderer. This class must process these events so // events are passed to the renderer. This class must process these events so
// that it can decide whether to consume the events and prevent them from // that it can decide whether to consume the events and prevent them from
// being passed to the renderer. This API is used to transition from kPending // being passed to the renderer. This API is used to transition from kPending
...@@ -113,13 +118,12 @@ enum RecognitionState { ...@@ -113,13 +118,12 @@ enum RecognitionState {
@private @private
// This controller will exist if and only if the UI is in history swipe mode. // This controller will exist if and only if the UI is in history swipe mode.
HistoryOverlayController* historyOverlay_; HistoryOverlayController* historyOverlay_;
// Each gesture received by the window is given a unique id.
// The id is monotonically increasing.
int currentGestureId_;
// The location of the fingers when the gesture started. // The location of the fingers when the gesture started.
NSPoint gestureStartPoint_; NSPoint gestureStartPoint_;
// The current location of the fingers in the gesture. // The current location of the fingers in the gesture.
NSPoint gestureCurrentPoint_; NSPoint gestureCurrentPoint_;
// The total Y distance moved since the beginning of the gesture.
CGFloat gestureTotalY_;
// A flag that indicates that there is an ongoing gesture. Only used to // A flag that indicates that there is an ongoing gesture. Only used to
// determine whether swipe events are coming from a Magic Mouse. // determine whether swipe events are coming from a Magic Mouse.
BOOL inGesture_; BOOL inGesture_;
...@@ -128,11 +132,13 @@ enum RecognitionState { ...@@ -128,11 +132,13 @@ enum RecognitionState {
// Each time a new gesture begins, we must get a new start point. // Each time a new gesture begins, we must get a new start point.
// This ivar determines whether the start point is valid. // This ivar determines whether the start point is valid.
int gestureStartPointValid_; int gestureStartPointValid_;
// The id of the last gesture that we processed as a history swipe.
int lastProcessedGestureId_; // The user's intended direction with the history swipe. Set during the
// A flag that indicates the user's intended direction with the history swipe. // transition from kPending -> kPotential.
history_swiper::NavigationDirection historySwipeDirection_; history_swiper::NavigationDirection historySwipeDirection_;
// A flag that indicates whether the gesture has its direction inverted.
// Whether the history swipe gesture has its direction inverted. Set during
// the transition from kPending -> kPotential.
BOOL historySwipeDirectionInverted_; BOOL historySwipeDirectionInverted_;
// Whether the event with phase NSEventPhaseBegan was not consumed by the // Whether the event with phase NSEventPhaseBegan was not consumed by the
......
...@@ -17,10 +17,8 @@ ...@@ -17,10 +17,8 @@
- (BOOL)browserCanNavigateInDirection: - (BOOL)browserCanNavigateInDirection:
(history_swiper::NavigationDirection)forward (history_swiper::NavigationDirection)forward
event:(NSEvent*)event; event:(NSEvent*)event;
- (void)endHistorySwipe; - (void)removeHistoryOverlay;
- (void)beginHistorySwipeInDirection: - (void)showHistoryOverlay:(history_swiper::NavigationDirection)direction;
(history_swiper::NavigationDirection)goForward
event:(NSEvent*)event;
- (void)navigateBrowserInDirection:(history_swiper::NavigationDirection)forward; - (void)navigateBrowserInDirection:(history_swiper::NavigationDirection)forward;
- (void)initiateMagicMouseHistorySwipe:(BOOL)isRightScroll - (void)initiateMagicMouseHistorySwipe:(BOOL)isRightScroll
event:(NSEvent*)event; event:(NSEvent*)event;
...@@ -51,22 +49,18 @@ class MacHistorySwiperTest : public CocoaTest { ...@@ -51,22 +49,18 @@ class MacHistorySwiperTest : public CocoaTest {
browserCanNavigateInDirection:history_swiper::kBackwards browserCanNavigateInDirection:history_swiper::kBackwards
event:[OCMArg any]]; event:[OCMArg any]];
[[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { [[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
++begin_count_; ++begin_count_;
// beginHistorySwipeInDirection: calls endHistorySwipe internally. // showHistoryOverlay: calls removeHistoryOverlay internally.
--end_count_; --end_count_;
}] andForwardToRealObject] }] andForwardToRealObject] showHistoryOverlay:history_swiper::kForwards];
beginHistorySwipeInDirection:history_swiper::kForwards
event:[OCMArg any]];
[[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { [[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
++begin_count_; ++begin_count_;
// beginHistorySwipeInDirection: calls endHistorySwipe internally. // showHistoryOverlay: calls removeHistoryOverlay internally.
--end_count_; --end_count_;
}] andForwardToRealObject] }] andForwardToRealObject] showHistoryOverlay:history_swiper::kBackwards];
beginHistorySwipeInDirection:history_swiper::kBackwards
event:[OCMArg any]];
[[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { [[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
++end_count_; ++end_count_;
}] andForwardToRealObject] endHistorySwipe]; }] andForwardToRealObject] removeHistoryOverlay];
[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { [[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
navigated_right_ = true; navigated_right_ = true;
}] navigateBrowserInDirection:history_swiper::kForwards]; }] navigateBrowserInDirection:history_swiper::kForwards];
...@@ -287,7 +281,7 @@ TEST_F(MacHistorySwiperTest, SwipeDiagonal) { ...@@ -287,7 +281,7 @@ TEST_F(MacHistorySwiperTest, SwipeDiagonal) {
moveGestureAtPoint(makePoint(0.6, 0.59)); moveGestureAtPoint(makePoint(0.6, 0.59));
endGestureAtPoint(makePoint(0.6, 0.59)); endGestureAtPoint(makePoint(0.6, 0.59));
EXPECT_EQ(begin_count_, 1); EXPECT_EQ(begin_count_, 0);
EXPECT_EQ(end_count_, 1); EXPECT_EQ(end_count_, 1);
EXPECT_FALSE(navigated_right_); EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_); EXPECT_FALSE(navigated_left_);
...@@ -380,7 +374,7 @@ TEST_F(MacHistorySwiperTest, NoSwipe) { ...@@ -380,7 +374,7 @@ TEST_F(MacHistorySwiperTest, NoSwipe) {
// No movement. // No movement.
endGestureAtPoint(makePoint(0.44, 0.44)); endGestureAtPoint(makePoint(0.44, 0.44));
EXPECT_EQ(begin_count_, 1); EXPECT_EQ(begin_count_, 0);
EXPECT_EQ(end_count_, 1); EXPECT_EQ(end_count_, 1);
EXPECT_FALSE(navigated_right_); EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_); EXPECT_FALSE(navigated_left_);
...@@ -451,3 +445,30 @@ TEST_F(MacHistorySwiperTest, SwipeRightEventOrdering) { ...@@ -451,3 +445,30 @@ TEST_F(MacHistorySwiperTest, SwipeRightEventOrdering) {
EXPECT_TRUE(navigated_right_); EXPECT_TRUE(navigated_right_);
EXPECT_FALSE(navigated_left_); EXPECT_FALSE(navigated_left_);
} }
// Substantial vertical scrolling followed by horizontal scrolling should not
// result in navigation.
TEST_F(MacHistorySwiperTest, SubstantialVerticalThenHorizontal) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
startGestureInMiddle();
moveGestureInMiddle();
// Move up, then move down.
for (CGFloat y = 0.51; y < 0.6; y += 0.01)
moveGestureAtPoint(makePoint(0.5, y));
for (CGFloat y = 0.59; y > 0.5; y -= 0.01)
moveGestureAtPoint(makePoint(0.5, y));
// Large movement to the right.
moveGestureAtPoint(makePoint(0.6, 0.51));
endGestureAtPoint(makePoint(0.6, 0.51));
EXPECT_EQ(begin_count_, 0);
EXPECT_EQ(end_count_, 1);
EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment