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

Fix Magic Mouse history swiping bug.

Magic Mouse gestures don't cause -touches*WithEvent: events to be sent down the
responder chain. As such, all state related to Magic Mouse gestures needs to be
reset during -beginGestureWithEvent:.

BUG=317161

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

Cr-Commit-Position: refs/heads/master@{#311762}
parent b1799751
......@@ -113,6 +113,18 @@ enum RecognitionState {
// 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
// -> kPotential.
//
// This class is also responsible for handling gestures from a Magic Mouse.
// Magic Mouse gestures do not generate -touches*WithEvent: callbacks, so this
// class must use the API -[NSEvent trackSwipeEventWithOptions:...] to track
// the Magic Mouse gesture. Due to an AppKit bug, once this API is invoked,
// views no longer reliable receive -touches*WithEvent: callbacks. As such,
// once this class invokes the -[NSEvent trackSwipeEventWithOptions:...] API,
// it must continue to use that API, since it no longer receives touch events.
//
// TODO(erikchen): Even for users that do not have a Magic Mouse, this class
// will sometime transition into Magic Mouse mode. This is very undesirable.
// See http://crbug.com/317161 for more details.
@class HistoryOverlayController;
@interface HistorySwiper : NSObject {
@private
......@@ -149,7 +161,7 @@ enum RecognitionState {
id<HistorySwiperDelegate> delegate_;
// Cumulative scroll delta since scroll gesture start. Only valid during
// scroll gesture handling. Used for history swiping.
// scroll gesture handling. Only used to trigger Magic Mouse history swiping.
NSSize mouseScrollDelta_;
}
......@@ -162,10 +174,14 @@ enum RecognitionState {
// The event passed in is a gesture event, and has touch data associated with
// the trackpad.
// Once the method -[NSEvent trackSwipeEventWithOptions:...] is invoked, the
// methods -touches*WithEvent: are no longer guaranteed to be called for
// subsequent gestures. http://crbug.com/317161
- (void)touchesBeganWithEvent:(NSEvent*)event;
- (void)touchesMovedWithEvent:(NSEvent*)event;
- (void)touchesCancelledWithEvent:(NSEvent*)event;
- (void)touchesEndedWithEvent:(NSEvent*)event;
- (void)beginGestureWithEvent:(NSEvent*)event;
- (void)endGestureWithEvent:(NSEvent*)event;
......
......@@ -163,6 +163,9 @@ BOOL forceMagicMouse = NO;
- (void)beginGestureWithEvent:(NSEvent*)event {
inGesture_ = YES;
// Reset state pertaining to Magic Mouse swipe gestures.
mouseScrollDelta_ = NSZeroSize;
}
- (void)endGestureWithEvent:(NSEvent*)event {
......@@ -213,10 +216,9 @@ BOOL forceMagicMouse = NO;
- (void)touchesBeganWithEvent:(NSEvent*)event {
receivingTouches_ = YES;
// Reset state pertaining to previous gestures.
// Reset state pertaining to previous trackpad gestures.
gestureStartPointValid_ = NO;
gestureTotalY_ = 0;
mouseScrollDelta_ = NSZeroSize;
beganEventUnconsumed_ = NO;
recognitionState_ = history_swiper::kPending;
}
......
......@@ -90,6 +90,7 @@ class MacHistorySwiperTest : public CocoaTest {
CocoaTest::TearDown();
}
// These methods send all 3 types of events: gesture, scroll, and touch.
void startGestureInMiddle();
void moveGestureInMiddle();
void moveGestureAtPoint(NSPoint point);
......@@ -97,6 +98,10 @@ class MacHistorySwiperTest : public CocoaTest {
void endGestureAtPoint(NSPoint point);
void rendererACKForBeganEvent();
// These methods send a single type of event.
void sendBeginGestureEventInMiddle();
void sendEndGestureEventAtPoint(NSPoint point);
HistorySwiper* historySwiper_;
NSView* view_;
int begin_count_;
......@@ -189,6 +194,8 @@ void MacHistorySwiperTest::endGestureAtPoint(NSPoint point) {
NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseEnded);
[historySwiper_ handleEvent:scrollEvent];
sendEndGestureEventAtPoint(point);
}
void MacHistorySwiperTest::rendererACKForBeganEvent() {
......@@ -197,6 +204,16 @@ void MacHistorySwiperTest::rendererACKForBeganEvent() {
[historySwiper_ rendererHandledWheelEvent:event consumed:NO];
}
void MacHistorySwiperTest::sendBeginGestureEventInMiddle() {
NSEvent* event = mockEventWithPoint(makePoint(0.5, 0.5), NSEventTypeGesture);
[historySwiper_ beginGestureWithEvent:event];
}
void MacHistorySwiperTest::sendEndGestureEventAtPoint(NSPoint point) {
NSEvent* event = mockEventWithPoint(point, NSEventTypeGesture);
[historySwiper_ endGestureWithEvent:event];
}
// Test that a simple left-swipe causes navigation.
TEST_F(MacHistorySwiperTest, SwipeLeft) {
// These tests require 10.7+ APIs.
......@@ -472,3 +489,59 @@ TEST_F(MacHistorySwiperTest, SubstantialVerticalThenHorizontal) {
EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_);
}
// Magic Mouse gestures don't send -touches*WithEvent: callbacks. The history
// swiper should still handle this gracefully. It should not turn vertical
// motion into history swipes.
TEST_F(MacHistorySwiperTest, MagicMouseStateResetsCorrectly) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
// Magic mouse events don't generate '-touches*WithEvent:' callbacks.
// Send the following events:
// - beginGesture
// - scrollWheel: (phase=Began)
// - scrollWheel: (phase=Changed), significant horizontal motion.
// - scrollWheel: (phase=Ended)
// - endGesture
sendBeginGestureEventInMiddle();
[historySwiper_ handleEvent:scrollWheelEventWithPhase(NSEventPhaseBegan)];
// Callback from Blink to set the relevant state for history swiping.
rendererACKForBeganEvent();
NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseChanged,
NSEventPhaseNone, 200.0, 0);
[historySwiper_ handleEvent:scrollEvent];
[historySwiper_ handleEvent:scrollWheelEventWithPhase(NSEventPhaseEnded)];
sendEndGestureEventAtPoint(makePoint(0.7, 0.5));
// Expect this sequence of events to trigger a magic mouse history swipe.
EXPECT_TRUE(magic_mouse_history_swipe_);
// Reset state.
magic_mouse_history_swipe_ = false;
// Send the following events:
// - beginGesture
// - scrollWheel: (phase=Began)
// - scrollWheel: (phase=Changed), significant vertical motion.
// - scrollWheel: (phase=Ended)
// - endGesture
sendBeginGestureEventInMiddle();
[historySwiper_ handleEvent:scrollWheelEventWithPhase(NSEventPhaseBegan)];
// Callback from Blink to set the relevant state for history swiping.
rendererACKForBeganEvent();
scrollEvent =
scrollWheelEventWithPhase(NSEventPhaseChanged, NSEventPhaseNone, 0, 20);
[historySwiper_ handleEvent:scrollEvent];
[historySwiper_ handleEvent:scrollWheelEventWithPhase(NSEventPhaseEnded)];
sendEndGestureEventAtPoint(makePoint(0.5, 0.7));
// Vertical motion should never trigger a history swipe!
EXPECT_FALSE(magic_mouse_history_swipe_);
}
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