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