chrome_render_widget_host_view_mac_history_swiper_browsertest.mm revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <Cocoa/Cocoa.h>
6
7#include "base/files/file_path.h"
8#include "base/logging.h"
9#include "base/mac/scoped_nsobject.h"
10#import "base/mac/sdk_forward_declarations.h"
11#include "base/message_loop/message_loop.h"
12#include "chrome/browser/ui/browser.h"
13#include "chrome/browser/ui/tabs/tab_strip_model.h"
14#include "chrome/test/base/in_process_browser_test.h"
15#include "chrome/test/base/ui_test_utils.h"
16#include "content/public/browser/render_view_host.h"
17#include "content/public/browser/render_widget_host_view.h"
18#include "content/public/browser/web_contents.h"
19#include "content/public/test/browser_test_utils.h"
20#import "third_party/ocmock/OCMock/OCMock.h"
21#import "third_party/ocmock/ocmock_extensions.h"
22#include "url/gurl.h"
23
24namespace {
25
26// Refers to how the event is going to be sent to the NSView. There are 3
27// relevant sets of APIs. The current code relies on all three sets of APIs.
28// There is significant information duplication between the three sets of APIs,
29// but the timing of the callbacks of the three APIs differ significantly.
30enum Deployment {
31  // -[NSView touchesBeganWithEvent:]
32  DEPLOYMENT_TOUCHES_BEGAN,
33  // -[NSView touchesMovedWithEvent:]
34  DEPLOYMENT_TOUCHES_MOVED,
35  // -[NSView touchesEndedWithEvent:]
36  DEPLOYMENT_TOUCHES_ENDED,
37  // -[NSView scrollWheel:]
38  DEPLOYMENT_SCROLL_WHEEL,
39  // -[NSView beginGestureWithEvent:]
40  DEPLOYMENT_GESTURE_BEGIN,
41  // -[NSView endGestureWithEvent:]
42  DEPLOYMENT_GESTURE_END,
43};
44
45}  // namespace
46
47// A wrapper object for events queued for replay.
48@interface QueuedEvent : NSObject {
49  BOOL _runMessageLoop;
50  Deployment _deployment;
51  NSEvent* _event;
52}
53// Whether the message loop should be run after this event has been replayed.
54@property(nonatomic, assign) BOOL runMessageLoop;
55// How this event should be replayed.
56@property(nonatomic, assign) Deployment deployment;
57// The event to be replayed.
58@property(nonatomic, retain) NSEvent* event;
59@end
60
61@implementation QueuedEvent
62@synthesize deployment = _deployment;
63@synthesize event = _event;
64@synthesize runMessageLoop = _runMessageLoop;
65- (void)dealloc {
66  [_event release];
67  [super dealloc];
68}
69@end
70
71class ChromeRenderWidgetHostViewMacHistorySwiperTest
72    : public InProcessBrowserTest {
73 public:
74  ChromeRenderWidgetHostViewMacHistorySwiperTest()
75      : event_queue_(), touch_(CGPointMake(0, 0)) {
76    const base::FilePath base_path(FILE_PATH_LITERAL("scroll"));
77    url1_ = ui_test_utils::GetTestUrl(
78        base_path, base::FilePath(FILE_PATH_LITERAL("text.html")));
79    url2_ = ui_test_utils::GetTestUrl(
80        base_path, base::FilePath(FILE_PATH_LITERAL("blank.html")));
81  }
82
83  virtual void SetUpOnMainThread() OVERRIDE {
84    event_queue_.reset([[NSMutableArray alloc] init]);
85    touch_ = CGPointMake(0.5, 0.5);
86
87    // Ensure that the navigation stack is not empty.
88    ui_test_utils::NavigateToURL(browser(), url1_);
89    ASSERT_EQ(url1_, GetWebContents()->GetURL());
90    ui_test_utils::NavigateToURL(browser(), url2_);
91    ASSERT_EQ(url2_, GetWebContents()->GetURL());
92  }
93
94  virtual void TearDownOnMainThread() OVERRIDE {
95    event_queue_.reset();
96  }
97
98 protected:
99  // Returns the active web contents.
100  content::WebContents* GetWebContents() {
101    return browser()->tab_strip_model()->GetActiveWebContents();
102  }
103
104  // Returns the value of |query| from Javascript as an int.
105  int GetScriptIntValue(const std::string& query) {
106    int value = 0;
107    EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
108        GetWebContents(),
109        "domAutomationController.send(" + query + ")",
110        &value));
111    return value;
112  }
113
114  // Returns the vertical scroll offset of the current page.
115  int GetScrollTop() {
116    return GetScriptIntValue("document.body.scrollTop");
117  }
118
119  bool IsHistorySwipingSupported() {
120    // These tests require 10.7+ APIs.
121    return [NSEvent
122        respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)];
123  }
124
125  // Create mock events --------------------------------------------------------
126
127  // Creates a mock scroll wheel event that is backed by a real CGEvent.
128  id MockScrollWheelEvent(NSPoint delta, NSEventType type) {
129    CGEventRef cg_event =
130        CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitLine, 2, 0, 0);
131    CGEventSetIntegerValueField(cg_event, kCGScrollWheelEventIsContinuous, 1);
132    CGEventSetIntegerValueField(
133        cg_event, kCGScrollWheelEventPointDeltaAxis2, delta.x);
134    CGEventSetIntegerValueField(
135        cg_event, kCGScrollWheelEventPointDeltaAxis1, delta.y);
136    NSEvent* event = [NSEvent eventWithCGEvent:cg_event];
137    CFRelease(cg_event);
138
139    id mock_event = [OCMockObject partialMockForObject:event];
140    [[[mock_event stub] andReturnBool:NO] isDirectionInvertedFromDevice];
141    [(NSEvent*)[[mock_event stub] andReturnValue:OCMOCK_VALUE(type)] type];
142
143    return mock_event;
144  }
145
146  // Returns a scroll wheel event with the given parameters.
147  id ScrollWheelEventWithPhase(NSEventPhase phase,
148                               NSEventPhase momentum_phase,
149                               CGFloat scrolling_delta_x,
150                               CGFloat scrolling_delta_y) {
151    id event = MockScrollWheelEvent(
152        NSMakePoint(scrolling_delta_x, scrolling_delta_y), NSScrollWheel);
153    [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(phase)] phase];
154    [(NSEvent*)[[event stub]
155        andReturnValue:OCMOCK_VALUE(momentum_phase)] momentumPhase];
156    [(NSEvent*)[[event stub]
157        andReturnValue:OCMOCK_VALUE(scrolling_delta_x)] scrollingDeltaX];
158    [(NSEvent*)[[event stub]
159        andReturnValue:OCMOCK_VALUE(scrolling_delta_y)] scrollingDeltaY];
160    NSUInteger modifierFlags = 0;
161    [(NSEvent*)[[event stub]
162        andReturnValue:OCMOCK_VALUE(modifierFlags)] modifierFlags];
163    NSView* view =
164        GetWebContents()->GetRenderViewHost()->GetView()->GetNativeView();
165    NSWindow* window = [view window];
166    [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(window)] window];
167
168    return event;
169  }
170
171  // Queue events for playback -------------------------------------------------
172
173  void QueueEvent(id event, Deployment deployment, BOOL run_message_loop) {
174    QueuedEvent* queued_event = [[[QueuedEvent alloc] init] autorelease];
175    queued_event.event = event;
176    queued_event.deployment = deployment;
177    queued_event.runMessageLoop = run_message_loop;
178    [event_queue_ addObject:queued_event];
179  }
180
181  // Queues a trackpad scroll event (e.g. [NSView scrollWheel:])
182  void QueueTrackpadScroll(int dx,
183                           int dy,
184                           NSEventPhase phase,
185                           BOOL run_message_loop) {
186    id event = ScrollWheelEventWithPhase(phase, NSEventPhaseNone, dx, dy);
187    QueueEvent(event, DEPLOYMENT_SCROLL_WHEEL, run_message_loop);
188  }
189
190  // Queues a gesture begin event (e.g. [NSView gestureDidBegin:])
191  void QueueGestureBegin() {
192    id event = [OCMockObject mockForClass:[NSEvent class]];
193    NSEventType type = NSEventTypeBeginGesture;
194    [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(type)] type];
195    QueueEvent(event, DEPLOYMENT_GESTURE_BEGIN, NO);
196  }
197
198  // Queues a gesture end event (e.g. [NSView gestureDidEnd:])
199  void QueueGestureEnd() {
200    id event = [OCMockObject mockForClass:[NSEvent class]];
201    NSEventType type = NSEventTypeEndGesture;
202    [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(type)] type];
203    QueueEvent(event, DEPLOYMENT_GESTURE_END, NO);
204  }
205
206  // Queues a touch event with absolute coordinates |x| and |y|.
207  void QueueTouch(CGFloat x,
208                  CGFloat y,
209                  Deployment deployment,
210                  NSEventType type,
211                  short subtype,
212                  BOOL run_message_loop) {
213    id event = [OCMockObject mockForClass:[NSEvent class]];
214    [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(type)] type];
215    [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(subtype)] subtype];
216
217    id mock_touch = [OCMockObject mockForClass:[NSTouch class]];
218    [[[mock_touch stub] andReturnNSPoint:NSMakePoint(x, y)] normalizedPosition];
219    NSArray* touches = @[ mock_touch ];
220    [[[event stub] andReturn:touches] touchesMatchingPhase:NSTouchPhaseAny
221                                                    inView:[OCMArg any]];
222    [[[event stub] andReturnBool:NO] isDirectionInvertedFromDevice];
223    QueueEvent(event, deployment, run_message_loop);
224  }
225
226  // Convenience methods for event queuing -------------------------------------
227
228  // Trackpad scroll events are roughly related to touch events. Given a
229  // trackpad scroll delta, approximate the change to the touch event.
230  void UpdateTouchLocationFromTrackpadScroll(int dx, int dy) {
231    touch_.x -= dx * 0.001;
232    touch_.y -= dy * 0.001;
233  }
234
235  // Queue the typical events at the beginning of a new swipe gesture. The
236  // ordering and values were determined by recording real swipe events.
237  void QueueBeginningEvents(int dx, int dy) {
238    QueueTouch(
239        DEPLOYMENT_TOUCHES_BEGAN, NSEventTypeGesture, NSMouseEventSubtype, NO);
240    QueueTrackpadScroll(0, 0, NSEventPhaseMayBegin, YES);
241    QueueTouch(
242        DEPLOYMENT_TOUCHES_MOVED, NSEventTypeGesture, NSMouseEventSubtype, NO);
243
244    QueueTrackpadScroll(dx, dy, NSEventPhaseBegan, NO);
245    QueueGestureBegin();
246    QueueTouch(DEPLOYMENT_TOUCHES_MOVED,
247               NSEventTypeBeginGesture,
248               NSTouchEventSubtype,
249               NO);
250    QueueTouch(
251        DEPLOYMENT_TOUCHES_MOVED, NSEventTypeGesture, NSTouchEventSubtype, YES);
252    UpdateTouchLocationFromTrackpadScroll(dx, dy);
253    QueueTouch(
254        DEPLOYMENT_TOUCHES_MOVED, NSEventTypeGesture, NSTouchEventSubtype, NO);
255  }
256
257  // Queue the typical events at the end of a new swipe gesture. The ordering
258  // and values were determined by recording real swipe events.
259  void QueueEndEvents() {
260    QueueTouch(DEPLOYMENT_TOUCHES_MOVED,
261               NSEventTypeEndGesture,
262               NSMouseEventSubtype,
263               NO);
264    QueueTouch(DEPLOYMENT_TOUCHES_ENDED,
265               NSEventTypeEndGesture,
266               NSMouseEventSubtype,
267               NO);
268    QueueGestureEnd();
269    QueueTrackpadScroll(0, 0, NSEventPhaseEnded, YES);
270  }
271
272  // Queues a trackpad scroll movement and a touch movement event.
273  void QueueScrollAndTouchMoved(int dx, int dy) {
274    QueueTrackpadScroll(dx, dy, NSEventPhaseChanged, NO);
275    UpdateTouchLocationFromTrackpadScroll(dx, dy);
276    QueueTouch(
277        DEPLOYMENT_TOUCHES_MOVED, NSEventTypeGesture, NSTouchEventSubtype, YES);
278  }
279
280  // Queues a touch event with the stored touch coordinates.
281  void QueueTouch(Deployment deployment,
282                  NSEventType type,
283                  short subtype,
284                  BOOL run_message_loop) {
285    QueueTouch(touch_.x, touch_.y, deployment, type, subtype, run_message_loop);
286  }
287
288  // Replays the events from the queue.
289  void RunQueuedEvents() {
290    while ([event_queue_ count] > 0) {
291      QueuedEvent* queued_event = [event_queue_ objectAtIndex:0];
292      NSEvent* event = queued_event.event;
293      NSView* view =
294          GetWebContents()->GetRenderViewHost()->GetView()->GetNativeView();
295      BOOL run_loop = queued_event.runMessageLoop;
296      switch (queued_event.deployment) {
297        case DEPLOYMENT_GESTURE_BEGIN:
298          [view beginGestureWithEvent:event];
299          break;
300        case DEPLOYMENT_GESTURE_END:
301          [view endGestureWithEvent:event];
302          break;
303        case DEPLOYMENT_SCROLL_WHEEL:
304          [view scrollWheel:event];
305          break;
306        case DEPLOYMENT_TOUCHES_BEGAN:
307          [view touchesBeganWithEvent:event];
308          break;
309        case DEPLOYMENT_TOUCHES_ENDED:
310          [view touchesEndedWithEvent:event];
311          break;
312        case DEPLOYMENT_TOUCHES_MOVED:
313          [view touchesMovedWithEvent:event];
314          break;
315      }
316
317      [event_queue_ removeObjectAtIndex:0];
318
319      if (!run_loop)
320        continue;
321      // Give time for the IPC to make it to the renderer process. If the IPC
322      // doesn't have time to make it to the renderer process, that's okay,
323      // since that simulates realistic conditions.
324      [[NSRunLoop currentRunLoop]
325          runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]];
326      // The renderer process returns an IPC, which needs to be handled.
327      base::MessageLoop::current()->RunUntilIdle();
328    }
329  }
330
331  void ExpectUrlAndOffset(const GURL& url, int offset) {
332    content::WaitForLoadStop(GetWebContents());
333    EXPECT_EQ(url, GetWebContents()->GetURL());
334
335    const int scroll_offset = GetScrollTop();
336    EXPECT_EQ(offset, scroll_offset);
337  }
338
339  GURL url1_;
340  GURL url2_;
341  base::scoped_nsobject<NSMutableArray> event_queue_;
342  // The current location of the user's fingers on the track pad.
343  CGPoint touch_;
344
345 private:
346  DISALLOW_COPY_AND_ASSIGN(ChromeRenderWidgetHostViewMacHistorySwiperTest);
347};
348
349// The ordering, timing, and parameters of the events was determined by
350// recording a real swipe.
351IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest,
352                       TestBackwardsHistoryNavigationRealData) {
353  if (!IsHistorySwipingSupported())
354    return;
355
356  QueueTouch(0.510681,
357             0.444672,
358             DEPLOYMENT_TOUCHES_BEGAN,
359             NSEventTypeGesture,
360             NSMouseEventSubtype,
361             NO);
362  QueueTrackpadScroll(0, 0, NSEventPhaseMayBegin, YES);
363  QueueTouch(0.510681,
364             0.444672,
365             DEPLOYMENT_TOUCHES_MOVED,
366             NSEventTypeGesture,
367             NSMouseEventSubtype,
368             NO);
369
370  QueueTrackpadScroll(1, 0, NSEventPhaseBegan, NO);
371  QueueGestureBegin();
372  QueueTouch(0.510681,
373             0.444672,
374             DEPLOYMENT_TOUCHES_MOVED,
375             NSEventTypeBeginGesture,
376             NSTouchEventSubtype,
377             NO);
378  QueueTouch(0.510681,
379             0.444672,
380             DEPLOYMENT_TOUCHES_MOVED,
381             NSEventTypeGesture,
382             NSTouchEventSubtype,
383             YES);
384
385  QueueTouch(0.507019,
386             0.444092,
387             DEPLOYMENT_TOUCHES_MOVED,
388             NSEventTypeGesture,
389             NSTouchEventSubtype,
390             NO);
391  QueueTrackpadScroll(3, 0, NSEventPhaseChanged, YES);
392
393  QueueTrackpadScroll(3, -1, NSEventPhaseChanged, NO);
394  QueueTouch(0.502861,
395             0.443512,
396             DEPLOYMENT_TOUCHES_MOVED,
397             NSEventTypeGesture,
398             NSTouchEventSubtype,
399             YES);
400
401  QueueTrackpadScroll(6, -1, NSEventPhaseChanged, NO);
402  QueueTouch(0.497002,
403             0.44294,
404             DEPLOYMENT_TOUCHES_MOVED,
405             NSEventTypeGesture,
406             NSTouchEventSubtype,
407             YES);
408
409  QueueTrackpadScroll(5, -1, NSEventPhaseChanged, NO);
410  QueueTouch(0.487236,
411             0.44149,
412             DEPLOYMENT_TOUCHES_MOVED,
413             NSEventTypeGesture,
414             NSTouchEventSubtype,
415             YES);
416
417  QueueTrackpadScroll(8, -1, NSEventPhaseChanged, NO);
418  QueueTouch(0.480392,
419             0.440628,
420             DEPLOYMENT_TOUCHES_MOVED,
421             NSEventTypeGesture,
422             NSTouchEventSubtype,
423             NO);
424  QueueTouch(0.475266,
425             0.440338,
426             DEPLOYMENT_TOUCHES_MOVED,
427             NSEventTypeGesture,
428             NSTouchEventSubtype,
429             YES);
430
431  QueueTrackpadScroll(6, -1, NSEventPhaseChanged, NO);
432  QueueTrackpadScroll(10, -1, NSEventPhaseChanged, NO);
433  QueueTouch(0.467934,
434             0.439758,
435             DEPLOYMENT_TOUCHES_MOVED,
436             NSEventTypeGesture,
437             NSTouchEventSubtype,
438             YES);
439
440  QueueTrackpadScroll(6, -1, NSEventPhaseChanged, NO);
441  QueueTouch(0.462807,
442             0.439186,
443             DEPLOYMENT_TOUCHES_MOVED,
444             NSEventTypeGesture,
445             NSTouchEventSubtype,
446             YES);
447  QueueTrackpadScroll(12, -1, NSEventPhaseChanged, NO);
448  QueueTouch(0.454018,
449             0.438316,
450             DEPLOYMENT_TOUCHES_MOVED,
451             NSEventTypeGesture,
452             NSTouchEventSubtype,
453             YES);
454
455  QueueTrackpadScroll(6, -1, NSEventPhaseChanged, NO);
456  QueueTouch(0.449623,
457             0.438026,
458             DEPLOYMENT_TOUCHES_MOVED,
459             NSEventTypeGesture,
460             NSTouchEventSubtype,
461             YES);
462
463  QueueTrackpadScroll(9, 0, NSEventPhaseChanged, NO);
464  QueueTouch(0.443275,
465             0.437744,
466             DEPLOYMENT_TOUCHES_MOVED,
467             NSEventTypeGesture,
468             NSTouchEventSubtype,
469             YES);
470  QueueTouch(0.437164,
471             0.437164,
472             DEPLOYMENT_TOUCHES_MOVED,
473             NSEventTypeGesture,
474             NSTouchEventSubtype,
475             YES);
476
477  QueueTrackpadScroll(9, -1, NSEventPhaseChanged, NO);
478  QueueTouch(0.431305,
479             0.436874,
480             DEPLOYMENT_TOUCHES_MOVED,
481             NSEventTypeGesture,
482             NSTouchEventSubtype,
483             YES);
484  QueueTrackpadScroll(8, -1, NSEventPhaseChanged, NO);
485  QueueTouch(0.425926,
486             0.436295,
487             DEPLOYMENT_TOUCHES_MOVED,
488             NSEventTypeGesture,
489             NSTouchEventSubtype,
490             YES);
491  QueueTrackpadScroll(7, -1, NSEventPhaseChanged, NO);
492  QueueTouch(0.420311,
493             0.43573,
494             DEPLOYMENT_TOUCHES_MOVED,
495             NSEventTypeGesture,
496             NSTouchEventSubtype,
497             YES);
498
499  QueueTrackpadScroll(7, -1, NSEventPhaseChanged, NO);
500  QueueTouch(0.415184,
501             0.43544,
502             DEPLOYMENT_TOUCHES_MOVED,
503             NSEventTypeGesture,
504             NSTouchEventSubtype,
505             YES);
506  QueueTrackpadScroll(6, -1, NSEventPhaseChanged, NO);
507  QueueTouch(0.410057,
508             0.43457,
509             DEPLOYMENT_TOUCHES_MOVED,
510             NSEventTypeGesture,
511             NSTouchEventSubtype,
512             YES);
513  QueueTouch(0.40493,
514             0.43399,
515             DEPLOYMENT_TOUCHES_MOVED,
516             NSEventTypeGesture,
517             NSTouchEventSubtype,
518             YES);
519  QueueTrackpadScroll(7, -1, NSEventPhaseChanged, YES);
520  QueueTrackpadScroll(3, -1, NSEventPhaseChanged, NO);
521  QueueTouch(0.402489,
522             0.433701,
523             DEPLOYMENT_TOUCHES_MOVED,
524             NSEventTypeGesture,
525             NSTouchEventSubtype,
526             YES);
527  QueueTrackpadScroll(5, 0, NSEventPhaseChanged, NO);
528  QueueTouch(0.398094,
529             0.433418,
530             DEPLOYMENT_TOUCHES_MOVED,
531             NSEventTypeGesture,
532             NSTouchEventSubtype,
533             YES);
534
535  QueueTrackpadScroll(4, -1, NSEventPhaseChanged, NO);
536  QueueTouch(0.394669,
537             0.433128,
538             DEPLOYMENT_TOUCHES_MOVED,
539             NSEventTypeGesture,
540             NSTouchEventSubtype,
541             YES);
542  QueueTouch(0.391006,
543             0.432549,
544             DEPLOYMENT_TOUCHES_MOVED,
545             NSEventTypeGesture,
546             NSTouchEventSubtype,
547             YES);
548  QueueTrackpadScroll(4, -1, NSEventPhaseChanged, NO);
549  QueueTrackpadScroll(5, 0, NSEventPhaseChanged, YES);
550  QueueTouch(0.386848,
551             0.432259,
552             DEPLOYMENT_TOUCHES_MOVED,
553             NSEventTypeGesture,
554             NSTouchEventSubtype,
555             YES);
556  QueueTouch(0.38343,
557             0.432259,
558             DEPLOYMENT_TOUCHES_MOVED,
559             NSEventTypeGesture,
560             NSTouchEventSubtype,
561             YES);
562
563  // Skipped a bunch of events. The data on the gesture end events are fudged.
564
565  QueueTouch(0.38343,
566             0.432259,
567             DEPLOYMENT_TOUCHES_MOVED,
568             NSEventTypeEndGesture,
569             NSMouseEventSubtype,
570             NO);
571  QueueTouch(0.38343,
572             0.432259,
573             DEPLOYMENT_TOUCHES_ENDED,
574             NSEventTypeEndGesture,
575             NSMouseEventSubtype,
576             NO);
577  QueueGestureEnd();
578  QueueTrackpadScroll(0, 0, NSEventPhaseEnded, YES);
579
580  RunQueuedEvents();
581  ExpectUrlAndOffset(url1_, 0);
582}
583
584// Each movement event that has non-zero parameters has both horizontal and
585// vertical motion. This should not trigger history navigation.
586IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest,
587                       TestAllDiagonalSwipes) {
588  if (!IsHistorySwipingSupported())
589    return;
590
591  QueueBeginningEvents(1, -1);
592  for (int i = 0; i < 150; ++i)
593    QueueScrollAndTouchMoved(1, -1);
594
595  QueueEndEvents();
596  RunQueuedEvents();
597  ExpectUrlAndOffset(url2_, 150);
598}
599
600// The movements are equal part diagonal, horizontal, and vertical. This should
601// not trigger history navigation.
602IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest,
603                       TestStaggeredDiagonalSwipe) {
604  if (!IsHistorySwipingSupported())
605    return;
606
607  QueueBeginningEvents(1, 0);
608  for (int i = 0; i < 150; ++i) {
609    switch (i % 3) {
610      case 0:
611        QueueScrollAndTouchMoved(1, -1);
612        break;
613      case 1:
614        QueueScrollAndTouchMoved(0, -1);
615        break;
616      case 2:
617        QueueScrollAndTouchMoved(1, 0);
618        break;
619      default:
620        NOTREACHED();
621    }
622  }
623
624  QueueEndEvents();
625  RunQueuedEvents();
626
627  content::WaitForLoadStop(GetWebContents());
628  EXPECT_EQ(url2_, GetWebContents()->GetURL());
629
630  // Depending on the timing of the IPCs, some of the initial events might be
631  // recognized as part of the history swipe, and not forwarded to the renderer,
632  // resulting in a non-deterministic scroll offset. This is bad, as some
633  // vertical motion is lost. Once the history swiper logic is fixed, this
634  // should become a direct comparison between 'scroll_offset' and 100.
635  // crbug.com/375514
636  const int scroll_offset = GetScrollTop();
637  // TODO(erikchen): Depending on the timing of the IPCs between Chrome and the
638  // renderer, more than 15% of the vertical motion can be lost. This assertion
639  // should eventually become an equality comparison against 100.
640  // crbug.com/378158
641  EXPECT_GT(scroll_offset, 1);
642}
643
644// The movement events are mostly in the horizontal direction, which should
645// trigger a history swipe. This should trigger history navigation.
646IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest,
647                       TestMostlyHorizontal) {
648  if (!IsHistorySwipingSupported())
649    return;
650
651  QueueBeginningEvents(1, -1);
652  for (int i = 0; i < 150; ++i) {
653    if (i % 10 == 0) {
654      QueueScrollAndTouchMoved(0, -1);
655    } else if (i % 5 == 0) {
656      QueueScrollAndTouchMoved(1, -1);
657    } else {
658      QueueScrollAndTouchMoved(1, 0);
659    }
660  }
661
662  QueueEndEvents();
663  RunQueuedEvents();
664  ExpectUrlAndOffset(url1_, 0);
665}
666
667// Each movement event is horizontal, except the first two. This should trigger
668// history navigation. This test is DISABLED because it has never worked. Once
669// the flaw in the history swiper logic has been corrected, this test should be
670// enabled.
671// crbug.com/375512
672IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest,
673                       DISABLED_TestAllHorizontalButFirst) {
674  if (!IsHistorySwipingSupported())
675    return;
676
677  QueueBeginningEvents(0, -1);
678  QueueScrollAndTouchMoved(0, -1);
679  for (int i = 0; i < 149; ++i)
680    QueueScrollAndTouchMoved(1, 0);
681
682  QueueEndEvents();
683  RunQueuedEvents();
684  ExpectUrlAndOffset(url1_, 0);
685}
686