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;
@class HistorySwiper;
@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.
- (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;
@end
......@@ -25,6 +25,11 @@ enum NavigationDirection {
kBackwards = 0,
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 {
// Waiting to see whether the renderer will handle the event with phase
// NSEventPhaseBegan. The state machine will also stay in this state if
......@@ -97,13 +102,13 @@ enum RecognitionState {
// then this class will transition to the state kTracking. Events are
// consumed, and not passed to the renderer.
//
// There are multiple APIs that provide information about gestures on the
// trackpad. This class uses two different set of APIs.
// 1. The -[NSView touches*WithEvent:] APIs provide detailed information
// There are multiple callbacks that provide information about gestures on the
// trackpad. This class uses two different sets of callbacks.
// 1. The -[NSView touches*WithEvent:] callbacks provide detailed information
// about the touches within a gesture. The callbacks happen with more
// frequency, and have higher accuracy. These APIs are used to transition
// between all state, exception for kPending -> kPotential.
// 2. The -[NSView scrollWheel:] API provides less information, but the
// frequency, and have higher accuracy. These callbacks are used to
// transition between all states except for kPending -> kPotential.
// 2. The -[NSView scrollWheel:] callback provides less information, but the
// 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
// being passed to the renderer. This API is used to transition from kPending
......@@ -113,13 +118,12 @@ enum RecognitionState {
@private
// This controller will exist if and only if the UI is in history swipe mode.
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.
NSPoint gestureStartPoint_;
// The current location of the fingers in the gesture.
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
// determine whether swipe events are coming from a Magic Mouse.
BOOL inGesture_;
......@@ -128,11 +132,13 @@ enum RecognitionState {
// Each time a new gesture begins, we must get a new start point.
// This ivar determines whether the start point is valid.
int gestureStartPointValid_;
// The id of the last gesture that we processed as a history swipe.
int lastProcessedGestureId_;
// A flag that indicates the user's intended direction with the history swipe.
// The user's intended direction with the history swipe. Set during the
// transition from kPending -> kPotential.
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_;
// Whether the event with phase NSEventPhaseBegan was not consumed by the
......
......@@ -17,10 +17,8 @@
- (BOOL)browserCanNavigateInDirection:
(history_swiper::NavigationDirection)forward
event:(NSEvent*)event;
- (void)endHistorySwipe;
- (void)beginHistorySwipeInDirection:
(history_swiper::NavigationDirection)goForward
event:(NSEvent*)event;
- (void)removeHistoryOverlay;
- (void)showHistoryOverlay:(history_swiper::NavigationDirection)direction;
- (void)navigateBrowserInDirection:(history_swiper::NavigationDirection)forward;
- (void)initiateMagicMouseHistorySwipe:(BOOL)isRightScroll
event:(NSEvent*)event;
......@@ -51,22 +49,18 @@ class MacHistorySwiperTest : public CocoaTest {
browserCanNavigateInDirection:history_swiper::kBackwards
event:[OCMArg any]];
[[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
++begin_count_;
// beginHistorySwipeInDirection: calls endHistorySwipe internally.
--end_count_;
}] andForwardToRealObject]
beginHistorySwipeInDirection:history_swiper::kForwards
event:[OCMArg any]];
++begin_count_;
// showHistoryOverlay: calls removeHistoryOverlay internally.
--end_count_;
}] andForwardToRealObject] showHistoryOverlay:history_swiper::kForwards];
[[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
++begin_count_;
// beginHistorySwipeInDirection: calls endHistorySwipe internally.
--end_count_;
}] andForwardToRealObject]
beginHistorySwipeInDirection:history_swiper::kBackwards
event:[OCMArg any]];
++begin_count_;
// showHistoryOverlay: calls removeHistoryOverlay internally.
--end_count_;
}] andForwardToRealObject] showHistoryOverlay:history_swiper::kBackwards];
[[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
++end_count_;
}] andForwardToRealObject] endHistorySwipe];
++end_count_;
}] andForwardToRealObject] removeHistoryOverlay];
[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
navigated_right_ = true;
}] navigateBrowserInDirection:history_swiper::kForwards];
......@@ -287,7 +281,7 @@ TEST_F(MacHistorySwiperTest, SwipeDiagonal) {
moveGestureAtPoint(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_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_);
......@@ -380,7 +374,7 @@ TEST_F(MacHistorySwiperTest, NoSwipe) {
// No movement.
endGestureAtPoint(makePoint(0.44, 0.44));
EXPECT_EQ(begin_count_, 1);
EXPECT_EQ(begin_count_, 0);
EXPECT_EQ(end_count_, 1);
EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_);
......@@ -451,3 +445,30 @@ TEST_F(MacHistorySwiperTest, SwipeRightEventOrdering) {
EXPECT_TRUE(navigated_right_);
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