| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <Cocoa/Cocoa.h> |
| |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/mac/scoped_nsobject.h" |
| #import "base/mac/sdk_forward_declarations.h" |
| #include "base/message_loop/message_loop.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/render_widget_host_view.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_test_utils.h" |
| #import "third_party/ocmock/OCMock/OCMock.h" |
| #import "third_party/ocmock/ocmock_extensions.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| |
| // Refers to how the event is going to be sent to the NSView. There are 3 |
| // relevant sets of APIs. The current code relies on all three sets of APIs. |
| // There is significant information duplication between the three sets of APIs, |
| // but the timing of the callbacks of the three APIs differ significantly. |
| enum Deployment { |
| // -[NSView touchesBeganWithEvent:] |
| DEPLOYMENT_TOUCHES_BEGAN, |
| // -[NSView touchesMovedWithEvent:] |
| DEPLOYMENT_TOUCHES_MOVED, |
| // -[NSView touchesEndedWithEvent:] |
| DEPLOYMENT_TOUCHES_ENDED, |
| // -[NSView scrollWheel:] |
| DEPLOYMENT_SCROLL_WHEEL, |
| // -[NSView beginGestureWithEvent:] |
| DEPLOYMENT_GESTURE_BEGIN, |
| // -[NSView endGestureWithEvent:] |
| DEPLOYMENT_GESTURE_END, |
| }; |
| |
| } // namespace |
| |
| // A wrapper object for events queued for replay. |
| @interface QueuedEvent : NSObject { |
| BOOL _runMessageLoop; |
| Deployment _deployment; |
| NSEvent* _event; |
| } |
| // Whether the message loop should be run after this event has been replayed. |
| @property(nonatomic, assign) BOOL runMessageLoop; |
| // How this event should be replayed. |
| @property(nonatomic, assign) Deployment deployment; |
| // The event to be replayed. |
| @property(nonatomic, retain) NSEvent* event; |
| @end |
| |
| @implementation QueuedEvent |
| @synthesize deployment = _deployment; |
| @synthesize event = _event; |
| @synthesize runMessageLoop = _runMessageLoop; |
| - (void)dealloc { |
| [_event release]; |
| [super dealloc]; |
| } |
| @end |
| |
| class ChromeRenderWidgetHostViewMacHistorySwiperTest |
| : public InProcessBrowserTest { |
| public: |
| ChromeRenderWidgetHostViewMacHistorySwiperTest() |
| : event_queue_(), touch_(CGPointMake(0, 0)) { |
| const base::FilePath base_path(FILE_PATH_LITERAL("scroll")); |
| url1_ = ui_test_utils::GetTestUrl( |
| base_path, base::FilePath(FILE_PATH_LITERAL("text.html"))); |
| url2_ = ui_test_utils::GetTestUrl( |
| base_path, base::FilePath(FILE_PATH_LITERAL("blank.html"))); |
| url_iframe_ = ui_test_utils::GetTestUrl( |
| base_path, base::FilePath(FILE_PATH_LITERAL("iframe.html"))); |
| } |
| |
| void SetUpOnMainThread() override { |
| event_queue_.reset([[NSMutableArray alloc] init]); |
| touch_ = CGPointMake(0.5, 0.5); |
| |
| // Ensure that the navigation stack is not empty. |
| ui_test_utils::NavigateToURL(browser(), url1_); |
| ASSERT_EQ(url1_, GetWebContents()->GetURL()); |
| ui_test_utils::NavigateToURL(browser(), url2_); |
| ASSERT_EQ(url2_, GetWebContents()->GetURL()); |
| } |
| |
| void TearDownOnMainThread() override { event_queue_.reset(); } |
| |
| protected: |
| // Returns the active web contents. |
| content::WebContents* GetWebContents() { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| // Returns the value of |query| from Javascript as an int. |
| int GetScriptIntValue(const std::string& query) { |
| int value = 0; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractInt( |
| GetWebContents(), |
| "domAutomationController.send(" + query + ")", |
| &value)); |
| return value; |
| } |
| |
| // Returns the vertical scroll offset of the current page. |
| int GetScrollTop() { |
| return GetScriptIntValue("document.body.scrollTop"); |
| } |
| |
| bool IsHistorySwipingSupported() { |
| // These tests require 10.7+ APIs. |
| return [NSEvent |
| respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]; |
| } |
| |
| // Create mock events -------------------------------------------------------- |
| |
| // Creates a mock scroll wheel event that is backed by a real CGEvent. |
| id MockScrollWheelEvent(NSPoint delta, NSEventType type) { |
| CGEventRef cg_event = |
| CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitLine, 2, 0, 0); |
| CGEventSetIntegerValueField(cg_event, kCGScrollWheelEventIsContinuous, 1); |
| CGEventSetIntegerValueField( |
| cg_event, kCGScrollWheelEventPointDeltaAxis2, delta.x); |
| CGEventSetIntegerValueField( |
| cg_event, kCGScrollWheelEventPointDeltaAxis1, delta.y); |
| NSEvent* event = [NSEvent eventWithCGEvent:cg_event]; |
| CFRelease(cg_event); |
| |
| id mock_event = [OCMockObject partialMockForObject:event]; |
| [[[mock_event stub] andReturnBool:NO] isDirectionInvertedFromDevice]; |
| [(NSEvent*)[[mock_event stub] andReturnValue:OCMOCK_VALUE(type)] type]; |
| |
| return mock_event; |
| } |
| |
| // Returns a scroll wheel event with the given parameters. |
| id ScrollWheelEventWithPhase(NSEventPhase phase, |
| NSEventPhase momentum_phase, |
| CGFloat scrolling_delta_x, |
| CGFloat scrolling_delta_y) { |
| id event = MockScrollWheelEvent( |
| NSMakePoint(scrolling_delta_x, scrolling_delta_y), NSScrollWheel); |
| [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(phase)] phase]; |
| [(NSEvent*)[[event stub] |
| andReturnValue:OCMOCK_VALUE(momentum_phase)] momentumPhase]; |
| [(NSEvent*)[[event stub] |
| andReturnValue:OCMOCK_VALUE(scrolling_delta_x)] scrollingDeltaX]; |
| [(NSEvent*)[[event stub] |
| andReturnValue:OCMOCK_VALUE(scrolling_delta_y)] scrollingDeltaY]; |
| NSUInteger modifierFlags = 0; |
| [(NSEvent*)[[event stub] |
| andReturnValue:OCMOCK_VALUE(modifierFlags)] modifierFlags]; |
| NSView* view = |
| GetWebContents()->GetRenderViewHost()->GetView()->GetNativeView(); |
| NSWindow* window = [view window]; |
| [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(window)] window]; |
| |
| return event; |
| } |
| |
| // Queue events for playback ------------------------------------------------- |
| |
| void QueueEvent(id event, Deployment deployment, BOOL run_message_loop) { |
| QueuedEvent* queued_event = [[[QueuedEvent alloc] init] autorelease]; |
| queued_event.event = event; |
| queued_event.deployment = deployment; |
| queued_event.runMessageLoop = run_message_loop; |
| [event_queue_ addObject:queued_event]; |
| } |
| |
| // Queues a trackpad scroll event (e.g. [NSView scrollWheel:]) |
| void QueueTrackpadScroll(int dx, |
| int dy, |
| NSEventPhase phase, |
| BOOL run_message_loop) { |
| id event = ScrollWheelEventWithPhase(phase, NSEventPhaseNone, dx, dy); |
| QueueEvent(event, DEPLOYMENT_SCROLL_WHEEL, run_message_loop); |
| } |
| |
| // Queues a gesture begin event (e.g. [NSView gestureDidBegin:]) |
| void QueueGestureBegin() { |
| id event = [OCMockObject mockForClass:[NSEvent class]]; |
| NSEventType type = NSEventTypeBeginGesture; |
| [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(type)] type]; |
| QueueEvent(event, DEPLOYMENT_GESTURE_BEGIN, NO); |
| } |
| |
| // Queues a gesture end event (e.g. [NSView gestureDidEnd:]) |
| void QueueGestureEnd() { |
| id event = [OCMockObject mockForClass:[NSEvent class]]; |
| NSEventType type = NSEventTypeEndGesture; |
| [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(type)] type]; |
| QueueEvent(event, DEPLOYMENT_GESTURE_END, NO); |
| } |
| |
| // Queues a touch event with absolute coordinates |x| and |y|. |
| void QueueTouch(CGFloat x, |
| CGFloat y, |
| Deployment deployment, |
| NSEventType type, |
| short subtype, |
| BOOL run_message_loop) { |
| id event = [OCMockObject mockForClass:[NSEvent class]]; |
| [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(type)] type]; |
| [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(subtype)] subtype]; |
| |
| id mock_touch = [OCMockObject mockForClass:[NSTouch class]]; |
| [[[mock_touch stub] andReturnNSPoint:NSMakePoint(x, y)] normalizedPosition]; |
| NSArray* touches = @[ mock_touch ]; |
| [[[event stub] andReturn:touches] touchesMatchingPhase:NSTouchPhaseAny |
| inView:[OCMArg any]]; |
| [[[event stub] andReturnBool:NO] isDirectionInvertedFromDevice]; |
| QueueEvent(event, deployment, run_message_loop); |
| } |
| |
| // Convenience methods for event queuing ------------------------------------- |
| |
| // Trackpad scroll events are roughly related to touch events. Given a |
| // trackpad scroll delta, approximate the change to the touch event. |
| void UpdateTouchLocationFromTrackpadScroll(int dx, int dy) { |
| touch_.x -= dx * 0.001; |
| touch_.y -= dy * 0.001; |
| } |
| |
| // Queue the typical events at the beginning of a new swipe gesture. The |
| // ordering and values were determined by recording real swipe events. |
| void QueueBeginningEvents(int dx, int dy) { |
| QueueTouch( |
| DEPLOYMENT_TOUCHES_BEGAN, NSEventTypeGesture, NSMouseEventSubtype, NO); |
| QueueTrackpadScroll(0, 0, NSEventPhaseMayBegin, YES); |
| QueueTouch( |
| DEPLOYMENT_TOUCHES_MOVED, NSEventTypeGesture, NSMouseEventSubtype, NO); |
| |
| QueueTrackpadScroll(dx, dy, NSEventPhaseBegan, NO); |
| QueueGestureBegin(); |
| QueueTouch(DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeBeginGesture, |
| NSTouchEventSubtype, |
| NO); |
| QueueTouch( |
| DEPLOYMENT_TOUCHES_MOVED, NSEventTypeGesture, NSTouchEventSubtype, YES); |
| UpdateTouchLocationFromTrackpadScroll(dx, dy); |
| QueueTouch( |
| DEPLOYMENT_TOUCHES_MOVED, NSEventTypeGesture, NSTouchEventSubtype, NO); |
| } |
| |
| // Queue the typical events at the end of a new swipe gesture. The ordering |
| // and values were determined by recording real swipe events. |
| void QueueEndEvents() { |
| QueueTouch(DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeEndGesture, |
| NSMouseEventSubtype, |
| NO); |
| QueueTouch(DEPLOYMENT_TOUCHES_ENDED, |
| NSEventTypeEndGesture, |
| NSMouseEventSubtype, |
| NO); |
| QueueGestureEnd(); |
| QueueTrackpadScroll(0, 0, NSEventPhaseEnded, YES); |
| } |
| |
| // Queues a trackpad scroll movement and a touch movement event. |
| void QueueScrollAndTouchMoved(int dx, int dy) { |
| QueueTrackpadScroll(dx, dy, NSEventPhaseChanged, NO); |
| UpdateTouchLocationFromTrackpadScroll(dx, dy); |
| QueueTouch( |
| DEPLOYMENT_TOUCHES_MOVED, NSEventTypeGesture, NSTouchEventSubtype, YES); |
| } |
| |
| // Queues a touch event with the stored touch coordinates. |
| void QueueTouch(Deployment deployment, |
| NSEventType type, |
| short subtype, |
| BOOL run_message_loop) { |
| QueueTouch(touch_.x, touch_.y, deployment, type, subtype, run_message_loop); |
| } |
| |
| // Replays the events from the queue. |
| void RunQueuedEvents() { |
| while ([event_queue_ count] > 0) { |
| QueuedEvent* queued_event = [event_queue_ objectAtIndex:0]; |
| NSEvent* event = queued_event.event; |
| NSView* view = |
| GetWebContents()->GetRenderViewHost()->GetView()->GetNativeView(); |
| BOOL run_loop = queued_event.runMessageLoop; |
| switch (queued_event.deployment) { |
| case DEPLOYMENT_GESTURE_BEGIN: |
| [view beginGestureWithEvent:event]; |
| break; |
| case DEPLOYMENT_GESTURE_END: |
| [view endGestureWithEvent:event]; |
| break; |
| case DEPLOYMENT_SCROLL_WHEEL: |
| [view scrollWheel:event]; |
| break; |
| case DEPLOYMENT_TOUCHES_BEGAN: |
| [view touchesBeganWithEvent:event]; |
| break; |
| case DEPLOYMENT_TOUCHES_ENDED: |
| [view touchesEndedWithEvent:event]; |
| break; |
| case DEPLOYMENT_TOUCHES_MOVED: |
| [view touchesMovedWithEvent:event]; |
| break; |
| } |
| |
| [event_queue_ removeObjectAtIndex:0]; |
| |
| if (!run_loop) |
| continue; |
| // Give time for the IPC to make it to the renderer process. If the IPC |
| // doesn't have time to make it to the renderer process, that's okay, |
| // since that simulates realistic conditions. |
| [[NSRunLoop currentRunLoop] |
| runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]]; |
| // The renderer process returns an IPC, which needs to be handled. |
| base::MessageLoop::current()->RunUntilIdle(); |
| } |
| } |
| |
| void ExpectUrlAndOffset(const GURL& url, int offset) { |
| content::WaitForLoadStop(GetWebContents()); |
| EXPECT_EQ(url, GetWebContents()->GetURL()); |
| |
| const int scroll_offset = GetScrollTop(); |
| EXPECT_EQ(offset, scroll_offset); |
| } |
| |
| GURL url1_; |
| GURL url2_; |
| GURL url_iframe_; |
| base::scoped_nsobject<NSMutableArray> event_queue_; |
| // The current location of the user's fingers on the track pad. |
| CGPoint touch_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ChromeRenderWidgetHostViewMacHistorySwiperTest); |
| }; |
| |
| // The ordering, timing, and parameters of the events was determined by |
| // recording a real swipe. |
| IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest, |
| TestBackwardsHistoryNavigationRealData) { |
| if (!IsHistorySwipingSupported()) |
| return; |
| |
| QueueTouch(0.510681, |
| 0.444672, |
| DEPLOYMENT_TOUCHES_BEGAN, |
| NSEventTypeGesture, |
| NSMouseEventSubtype, |
| NO); |
| QueueTrackpadScroll(0, 0, NSEventPhaseMayBegin, YES); |
| QueueTouch(0.510681, |
| 0.444672, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSMouseEventSubtype, |
| NO); |
| |
| QueueTrackpadScroll(1, 0, NSEventPhaseBegan, NO); |
| QueueGestureBegin(); |
| QueueTouch(0.510681, |
| 0.444672, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeBeginGesture, |
| NSTouchEventSubtype, |
| NO); |
| QueueTouch(0.510681, |
| 0.444672, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTouch(0.507019, |
| 0.444092, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| NO); |
| QueueTrackpadScroll(3, 0, NSEventPhaseChanged, YES); |
| |
| QueueTrackpadScroll(3, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.502861, |
| 0.443512, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTrackpadScroll(6, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.497002, |
| 0.44294, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTrackpadScroll(5, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.487236, |
| 0.44149, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTrackpadScroll(8, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.480392, |
| 0.440628, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| NO); |
| QueueTouch(0.475266, |
| 0.440338, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTrackpadScroll(6, -1, NSEventPhaseChanged, NO); |
| QueueTrackpadScroll(10, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.467934, |
| 0.439758, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTrackpadScroll(6, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.462807, |
| 0.439186, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTrackpadScroll(12, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.454018, |
| 0.438316, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTrackpadScroll(6, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.449623, |
| 0.438026, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTrackpadScroll(9, 0, NSEventPhaseChanged, NO); |
| QueueTouch(0.443275, |
| 0.437744, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTouch(0.437164, |
| 0.437164, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTrackpadScroll(9, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.431305, |
| 0.436874, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTrackpadScroll(8, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.425926, |
| 0.436295, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTrackpadScroll(7, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.420311, |
| 0.43573, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTrackpadScroll(7, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.415184, |
| 0.43544, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTrackpadScroll(6, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.410057, |
| 0.43457, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTouch(0.40493, |
| 0.43399, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTrackpadScroll(7, -1, NSEventPhaseChanged, YES); |
| QueueTrackpadScroll(3, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.402489, |
| 0.433701, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTrackpadScroll(5, 0, NSEventPhaseChanged, NO); |
| QueueTouch(0.398094, |
| 0.433418, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| QueueTrackpadScroll(4, -1, NSEventPhaseChanged, NO); |
| QueueTouch(0.394669, |
| 0.433128, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTouch(0.391006, |
| 0.432549, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTrackpadScroll(4, -1, NSEventPhaseChanged, NO); |
| QueueTrackpadScroll(5, 0, NSEventPhaseChanged, YES); |
| QueueTouch(0.386848, |
| 0.432259, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| QueueTouch(0.38343, |
| 0.432259, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeGesture, |
| NSTouchEventSubtype, |
| YES); |
| |
| // Skipped a bunch of events. The data on the gesture end events are fudged. |
| |
| QueueTouch(0.38343, |
| 0.432259, |
| DEPLOYMENT_TOUCHES_MOVED, |
| NSEventTypeEndGesture, |
| NSMouseEventSubtype, |
| NO); |
| QueueTouch(0.38343, |
| 0.432259, |
| DEPLOYMENT_TOUCHES_ENDED, |
| NSEventTypeEndGesture, |
| NSMouseEventSubtype, |
| NO); |
| QueueGestureEnd(); |
| QueueTrackpadScroll(0, 0, NSEventPhaseEnded, YES); |
| |
| RunQueuedEvents(); |
| ExpectUrlAndOffset(url1_, 0); |
| } |
| |
| // Each movement event that has non-zero parameters has both horizontal and |
| // vertical motion. This should not trigger history navigation. |
| // http://crbug.com/396328 |
| IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest, |
| DISABLED_TestAllDiagonalSwipes) { |
| if (!IsHistorySwipingSupported()) |
| return; |
| |
| QueueBeginningEvents(1, -1); |
| for (int i = 0; i < 150; ++i) |
| QueueScrollAndTouchMoved(1, -1); |
| |
| QueueEndEvents(); |
| RunQueuedEvents(); |
| ExpectUrlAndOffset(url2_, 150); |
| } |
| |
| // The movements are equal part diagonal, horizontal, and vertical. This should |
| // not trigger history navigation. |
| IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest, |
| TestStaggeredDiagonalSwipe) { |
| if (!IsHistorySwipingSupported()) |
| return; |
| |
| QueueBeginningEvents(1, 0); |
| for (int i = 0; i < 150; ++i) { |
| switch (i % 3) { |
| case 0: |
| QueueScrollAndTouchMoved(1, -1); |
| break; |
| case 1: |
| QueueScrollAndTouchMoved(0, -1); |
| break; |
| case 2: |
| QueueScrollAndTouchMoved(1, 0); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| QueueEndEvents(); |
| RunQueuedEvents(); |
| |
| content::WaitForLoadStop(GetWebContents()); |
| EXPECT_EQ(url2_, GetWebContents()->GetURL()); |
| |
| // Depending on the timing of the IPCs, some of the initial events might be |
| // recognized as part of the history swipe, and not forwarded to the renderer, |
| // resulting in a non-deterministic scroll offset. This is bad, as some |
| // vertical motion is lost. Once the history swiper logic is fixed, this |
| // should become a direct comparison between 'scroll_offset' and 100. |
| // crbug.com/375514 |
| const int scroll_offset = GetScrollTop(); |
| // TODO(erikchen): Depending on the timing of the IPCs between Chrome and the |
| // renderer, more than 15% of the vertical motion can be lost. This assertion |
| // should eventually become an equality comparison against 100. |
| // crbug.com/378158 |
| EXPECT_GT(scroll_offset, 1); |
| } |
| |
| // The movement events are mostly in the horizontal direction, which should |
| // trigger a history swipe. This should trigger history navigation. |
| IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest, |
| TestMostlyHorizontal) { |
| if (!IsHistorySwipingSupported()) |
| return; |
| |
| QueueBeginningEvents(1, -1); |
| for (int i = 0; i < 150; ++i) { |
| if (i % 10 == 0) { |
| QueueScrollAndTouchMoved(0, -1); |
| } else if (i % 5 == 0) { |
| QueueScrollAndTouchMoved(1, -1); |
| } else { |
| QueueScrollAndTouchMoved(1, 0); |
| } |
| } |
| |
| QueueEndEvents(); |
| RunQueuedEvents(); |
| ExpectUrlAndOffset(url1_, 0); |
| } |
| |
| // Each movement event is horizontal, except the first two. This should trigger |
| // history navigation. This test is DISABLED because it has never worked. Once |
| // the flaw in the history swiper logic has been corrected, this test should be |
| // enabled. |
| // crbug.com/375512 |
| IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest, |
| DISABLED_TestAllHorizontalButFirst) { |
| if (!IsHistorySwipingSupported()) |
| return; |
| |
| QueueBeginningEvents(0, -1); |
| QueueScrollAndTouchMoved(0, -1); |
| for (int i = 0; i < 149; ++i) |
| QueueScrollAndTouchMoved(1, 0); |
| |
| QueueEndEvents(); |
| RunQueuedEvents(); |
| ExpectUrlAndOffset(url1_, 0); |
| } |
| |
| // Initial movements are vertical, and scroll the iframe. Subsequent movements |
| // are horizontal, and should not trigger history swiping. |
| IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest, |
| TestIframeHistorySwiping) { |
| if (!IsHistorySwipingSupported()) |
| return; |
| |
| ui_test_utils::NavigateToURL(browser(), url_iframe_); |
| ASSERT_EQ(url_iframe_, GetWebContents()->GetURL()); |
| QueueBeginningEvents(0, -1); |
| for (int i = 0; i < 10; ++i) |
| QueueScrollAndTouchMoved(0, -1); |
| for (int i = 0; i < 149; ++i) |
| QueueScrollAndTouchMoved(1, 0); |
| |
| QueueEndEvents(); |
| RunQueuedEvents(); |
| content::WaitForLoadStop(GetWebContents()); |
| EXPECT_EQ(url_iframe_, GetWebContents()->GetURL()); |
| } |
| |
| // The gesture ends before the touchesEndedWithEvent: method gets called. |
| IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest, |
| TestGestureEndTiming) { |
| if (!IsHistorySwipingSupported()) |
| return; |
| |
| QueueBeginningEvents(1, 0); |
| for (int i = 0; i < 150; ++i) |
| QueueScrollAndTouchMoved(1, 0); |
| |
| QueueTouch( |
| DEPLOYMENT_TOUCHES_MOVED, NSEventTypeEndGesture, NSMouseEventSubtype, NO); |
| QueueGestureEnd(); |
| QueueTouch( |
| DEPLOYMENT_TOUCHES_ENDED, NSEventTypeEndGesture, NSMouseEventSubtype, NO); |
| QueueTrackpadScroll(0, 0, NSEventPhaseEnded, YES); |
| |
| RunQueuedEvents(); |
| ExpectUrlAndOffset(url1_, 0); |
| } |