Home | History | Annotate | Download | only in renderer_host
      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 
     24 namespace {
     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.
     30 enum 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 
     71 class 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.
    354 IN_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
    590 IN_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.
    606 IN_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.
    650 IN_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
    676 IN_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.
    693 IN_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.
    713 IN_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