Home | History | Annotate | Download | only in wm
      1 // Copyright (c) 2012 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 "ash/wm/system_gesture_event_filter.h"
      6 
      7 #include <vector>
      8 
      9 #include "ash/accelerators/accelerator_controller.h"
     10 #include "ash/display/display_manager.h"
     11 #include "ash/shelf/shelf.h"
     12 #include "ash/shelf/shelf_model.h"
     13 #include "ash/shell.h"
     14 #include "ash/system/tray/system_tray_delegate.h"
     15 #include "ash/test/ash_test_base.h"
     16 #include "ash/test/display_manager_test_api.h"
     17 #include "ash/test/shell_test_api.h"
     18 #include "ash/test/test_shelf_delegate.h"
     19 #include "ash/wm/gestures/long_press_affordance_handler.h"
     20 #include "ash/wm/window_state.h"
     21 #include "ash/wm/window_util.h"
     22 #include "base/time/time.h"
     23 #include "base/timer/timer.h"
     24 #include "ui/aura/env.h"
     25 #include "ui/aura/test/event_generator.h"
     26 #include "ui/aura/test/test_window_delegate.h"
     27 #include "ui/aura/test/test_windows.h"
     28 #include "ui/aura/window_event_dispatcher.h"
     29 #include "ui/base/hit_test.h"
     30 #include "ui/events/event.h"
     31 #include "ui/events/event_handler.h"
     32 #include "ui/events/event_utils.h"
     33 #include "ui/events/gestures/gesture_configuration.h"
     34 #include "ui/events/test/test_event_handler.h"
     35 #include "ui/gfx/screen.h"
     36 #include "ui/gfx/size.h"
     37 #include "ui/views/widget/widget.h"
     38 #include "ui/views/widget/widget_delegate.h"
     39 #include "ui/views/window/non_client_view.h"
     40 #include "ui/views/window/window_button_order_provider.h"
     41 
     42 namespace ash {
     43 namespace test {
     44 
     45 namespace {
     46 
     47 class ResizableWidgetDelegate : public views::WidgetDelegateView {
     48  public:
     49   ResizableWidgetDelegate() {}
     50   virtual ~ResizableWidgetDelegate() {}
     51 
     52  private:
     53   virtual bool CanResize() const OVERRIDE { return true; }
     54   virtual bool CanMaximize() const OVERRIDE { return true; }
     55   virtual void DeleteDelegate() OVERRIDE { delete this; }
     56 
     57   DISALLOW_COPY_AND_ASSIGN(ResizableWidgetDelegate);
     58 };
     59 
     60 // Support class for testing windows with a maximum size.
     61 class MaxSizeNCFV : public views::NonClientFrameView {
     62  public:
     63   MaxSizeNCFV() {}
     64  private:
     65   virtual gfx::Size GetMaximumSize() const OVERRIDE {
     66     return gfx::Size(200, 200);
     67   }
     68   virtual gfx::Rect GetBoundsForClientView() const OVERRIDE {
     69     return gfx::Rect();
     70   };
     71 
     72   virtual gfx::Rect GetWindowBoundsForClientBounds(
     73       const gfx::Rect& client_bounds) const OVERRIDE {
     74     return gfx::Rect();
     75   };
     76 
     77   // This function must ask the ClientView to do a hittest.  We don't do this in
     78   // the parent NonClientView because that makes it more difficult to calculate
     79   // hittests for regions that are partially obscured by the ClientView, e.g.
     80   // HTSYSMENU.
     81   virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE {
     82     return HTNOWHERE;
     83   }
     84   virtual void GetWindowMask(const gfx::Size& size,
     85                              gfx::Path* window_mask) OVERRIDE {}
     86   virtual void ResetWindowControls() OVERRIDE {}
     87   virtual void UpdateWindowIcon() OVERRIDE {}
     88   virtual void UpdateWindowTitle() OVERRIDE {}
     89 
     90   DISALLOW_COPY_AND_ASSIGN(MaxSizeNCFV);
     91 };
     92 
     93 class MaxSizeWidgetDelegate : public views::WidgetDelegateView {
     94  public:
     95   MaxSizeWidgetDelegate() {}
     96   virtual ~MaxSizeWidgetDelegate() {}
     97 
     98  private:
     99   virtual bool CanResize() const OVERRIDE { return true; }
    100   virtual bool CanMaximize() const OVERRIDE { return false; }
    101   virtual void DeleteDelegate() OVERRIDE { delete this; }
    102   virtual views::NonClientFrameView* CreateNonClientFrameView(
    103       views::Widget* widget) OVERRIDE {
    104     return new MaxSizeNCFV;
    105   }
    106 
    107   DISALLOW_COPY_AND_ASSIGN(MaxSizeWidgetDelegate);
    108 };
    109 
    110 } // namespace
    111 
    112 class SystemGestureEventFilterTest : public AshTestBase {
    113  public:
    114   SystemGestureEventFilterTest() : AshTestBase() {}
    115   virtual ~SystemGestureEventFilterTest() {}
    116 
    117   LongPressAffordanceHandler* GetLongPressAffordance() {
    118     ShellTestApi shell_test(Shell::GetInstance());
    119     return shell_test.system_gesture_event_filter()->
    120         long_press_affordance_.get();
    121   }
    122 
    123   base::OneShotTimer<LongPressAffordanceHandler>*
    124   GetLongPressAffordanceTimer() {
    125     return &GetLongPressAffordance()->timer_;
    126   }
    127 
    128   aura::Window* GetLongPressAffordanceTarget() {
    129     return GetLongPressAffordance()->tap_down_target_;
    130   }
    131 
    132   views::View* GetLongPressAffordanceView() {
    133     return reinterpret_cast<views::View*>(
    134         GetLongPressAffordance()->view_.get());
    135   }
    136 
    137   // Overridden from AshTestBase:
    138   virtual void SetUp() OVERRIDE {
    139     // TODO(jonross): TwoFingerDragDelayed() and ThreeFingerGestureStopsDrag()
    140     // both use hardcoded touch points, assuming that they target empty header
    141     // space. Window control order now reflects configuration files and can
    142     // change. The tests should be improved to dynamically decide touch points.
    143     // To address this we specify the originally expected window control
    144     // positions to be consistent across tests.
    145     std::vector<views::FrameButton> leading;
    146     std::vector<views::FrameButton> trailing;
    147     trailing.push_back(views::FRAME_BUTTON_MINIMIZE);
    148     trailing.push_back(views::FRAME_BUTTON_MAXIMIZE);
    149     trailing.push_back(views::FRAME_BUTTON_CLOSE);
    150     views::WindowButtonOrderProvider::GetInstance()->
    151         SetWindowButtonOrder(leading, trailing);
    152 
    153     test::AshTestBase::SetUp();
    154     // Enable brightness key.
    155     test::DisplayManagerTestApi(Shell::GetInstance()->display_manager()).
    156         SetFirstDisplayAsInternalDisplay();
    157   }
    158 
    159  private:
    160   DISALLOW_COPY_AND_ASSIGN(SystemGestureEventFilterTest);
    161 };
    162 
    163 ui::GestureEvent* CreateGesture(ui::EventType type,
    164                                     int x,
    165                                     int y,
    166                                     float delta_x,
    167                                     float delta_y,
    168                                     int touch_id) {
    169   return new ui::GestureEvent(type, x, y, 0,
    170       base::TimeDelta::FromMilliseconds(base::Time::Now().ToDoubleT() * 1000),
    171       ui::GestureEventDetails(type, delta_x, delta_y), 1 << touch_id);
    172 }
    173 
    174 TEST_F(SystemGestureEventFilterTest, LongPressAffordanceStateOnCaptureLoss) {
    175   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    176 
    177   aura::test::TestWindowDelegate delegate;
    178   scoped_ptr<aura::Window> window0(
    179       aura::test::CreateTestWindowWithDelegate(
    180           &delegate, 9, gfx::Rect(0, 0, 100, 100), root_window));
    181   scoped_ptr<aura::Window> window1(
    182       aura::test::CreateTestWindowWithDelegate(
    183           &delegate, 10, gfx::Rect(0, 0, 100, 50), window0.get()));
    184   scoped_ptr<aura::Window> window2(
    185       aura::test::CreateTestWindowWithDelegate(
    186           &delegate, 11, gfx::Rect(0, 50, 100, 50), window0.get()));
    187 
    188   const int kTouchId = 5;
    189 
    190   // Capture first window.
    191   window1->SetCapture();
    192   EXPECT_TRUE(window1->HasCapture());
    193 
    194   // Send touch event to first window.
    195   ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
    196                        gfx::Point(10, 10),
    197                        kTouchId,
    198                        ui::EventTimeForNow());
    199   ui::EventDispatchDetails details =
    200       root_window->GetHost()->dispatcher()->OnEventFromSource(&press);
    201   ASSERT_FALSE(details.dispatcher_destroyed);
    202   EXPECT_TRUE(window1->HasCapture());
    203 
    204   base::OneShotTimer<LongPressAffordanceHandler>* timer =
    205       GetLongPressAffordanceTimer();
    206   EXPECT_TRUE(timer->IsRunning());
    207   EXPECT_EQ(window1, GetLongPressAffordanceTarget());
    208 
    209   // Force timeout so that the affordance animation can start.
    210   timer->user_task().Run();
    211   timer->Stop();
    212   EXPECT_TRUE(GetLongPressAffordance()->is_animating());
    213 
    214   // Change capture.
    215   window2->SetCapture();
    216   EXPECT_TRUE(window2->HasCapture());
    217 
    218   EXPECT_TRUE(GetLongPressAffordance()->is_animating());
    219   EXPECT_EQ(window1, GetLongPressAffordanceTarget());
    220 
    221   // Animate to completion.
    222   GetLongPressAffordance()->End();  // end grow animation.
    223   // Force timeout to start shrink animation.
    224   EXPECT_TRUE(timer->IsRunning());
    225   timer->user_task().Run();
    226   timer->Stop();
    227   EXPECT_TRUE(GetLongPressAffordance()->is_animating());
    228   GetLongPressAffordance()->End();  // end shrink animation.
    229 
    230   // Check if state has reset.
    231   EXPECT_EQ(NULL, GetLongPressAffordanceTarget());
    232   EXPECT_EQ(NULL, GetLongPressAffordanceView());
    233 }
    234 
    235 TEST_F(SystemGestureEventFilterTest, TwoFingerDrag) {
    236   gfx::Rect bounds(0, 0, 600, 600);
    237   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    238   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    239       new ResizableWidgetDelegate, root_window, bounds);
    240   toplevel->Show();
    241 
    242   const int kSteps = 15;
    243   const int kTouchPoints = 2;
    244   gfx::Point points[kTouchPoints] = {
    245     gfx::Point(250, 250),
    246     gfx::Point(350, 350),
    247   };
    248 
    249   aura::test::EventGenerator generator(root_window,
    250                                        toplevel->GetNativeWindow());
    251 
    252   wm::WindowState* toplevel_state =
    253       wm::GetWindowState(toplevel->GetNativeWindow());
    254   // Swipe down to minimize.
    255   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150);
    256   EXPECT_TRUE(toplevel_state->IsMinimized());
    257 
    258   toplevel->Restore();
    259   toplevel->GetNativeWindow()->SetBounds(bounds);
    260 
    261   // Swipe up to maximize.
    262   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, -150);
    263   EXPECT_TRUE(toplevel_state->IsMaximized());
    264 
    265   toplevel->Restore();
    266   toplevel->GetNativeWindow()->SetBounds(bounds);
    267 
    268   // Swipe right to snap.
    269   gfx::Rect normal_bounds = toplevel->GetWindowBoundsInScreen();
    270   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0);
    271   gfx::Rect right_tile_bounds = toplevel->GetWindowBoundsInScreen();
    272   EXPECT_NE(normal_bounds.ToString(), right_tile_bounds.ToString());
    273 
    274   // Swipe left to snap.
    275   gfx::Point left_points[kTouchPoints];
    276   for (int i = 0; i < kTouchPoints; ++i) {
    277     left_points[i] = points[i];
    278     left_points[i].Offset(right_tile_bounds.x(), right_tile_bounds.y());
    279   }
    280   generator.GestureMultiFingerScroll(kTouchPoints, left_points, 15, kSteps,
    281       -150, 0);
    282   gfx::Rect left_tile_bounds = toplevel->GetWindowBoundsInScreen();
    283   EXPECT_NE(normal_bounds.ToString(), left_tile_bounds.ToString());
    284   EXPECT_NE(right_tile_bounds.ToString(), left_tile_bounds.ToString());
    285 
    286   // Swipe right again.
    287   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0);
    288   gfx::Rect current_bounds = toplevel->GetWindowBoundsInScreen();
    289   EXPECT_NE(current_bounds.ToString(), left_tile_bounds.ToString());
    290   EXPECT_EQ(current_bounds.ToString(), right_tile_bounds.ToString());
    291 }
    292 
    293 TEST_F(SystemGestureEventFilterTest, TwoFingerDragTwoWindows) {
    294   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    295   ui::GestureConfiguration::set_max_separation_for_gesture_touches_in_pixels(0);
    296   views::Widget* first = views::Widget::CreateWindowWithContextAndBounds(
    297       new ResizableWidgetDelegate, root_window, gfx::Rect(10, 0, 50, 100));
    298   first->Show();
    299   views::Widget* second = views::Widget::CreateWindowWithContextAndBounds(
    300       new ResizableWidgetDelegate, root_window, gfx::Rect(100, 0, 100, 100));
    301   second->Show();
    302 
    303   // Start a two-finger drag on |first|, and then try to use another two-finger
    304   // drag to move |second|. The attempt to move |second| should fail.
    305   const gfx::Rect& first_bounds = first->GetWindowBoundsInScreen();
    306   const gfx::Rect& second_bounds = second->GetWindowBoundsInScreen();
    307   const int kSteps = 15;
    308   const int kTouchPoints = 4;
    309   gfx::Point points[kTouchPoints] = {
    310     first_bounds.origin() + gfx::Vector2d(5, 5),
    311     first_bounds.origin() + gfx::Vector2d(30, 10),
    312     second_bounds.origin() + gfx::Vector2d(5, 5),
    313     second_bounds.origin() + gfx::Vector2d(40, 20)
    314   };
    315 
    316   aura::test::EventGenerator generator(root_window);
    317   // Do not drag too fast to avoid fling.
    318   generator.GestureMultiFingerScroll(kTouchPoints, points,
    319       50, kSteps, 0, 150);
    320 
    321   EXPECT_NE(first_bounds.ToString(),
    322             first->GetWindowBoundsInScreen().ToString());
    323   EXPECT_EQ(second_bounds.ToString(),
    324             second->GetWindowBoundsInScreen().ToString());
    325 }
    326 
    327 TEST_F(SystemGestureEventFilterTest, WindowsWithMaxSizeDontSnap) {
    328   gfx::Rect bounds(250, 150, 100, 100);
    329   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    330   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    331       new MaxSizeWidgetDelegate, root_window, bounds);
    332   toplevel->Show();
    333 
    334   const int kSteps = 15;
    335   const int kTouchPoints = 2;
    336   gfx::Point points[kTouchPoints] = {
    337     gfx::Point(bounds.x() + 10, bounds.y() + 30),
    338     gfx::Point(bounds.x() + 30, bounds.y() + 20),
    339   };
    340 
    341   aura::test::EventGenerator generator(root_window,
    342                                        toplevel->GetNativeWindow());
    343 
    344   // Swipe down to minimize.
    345   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150);
    346   wm::WindowState* toplevel_state =
    347       wm::GetWindowState(toplevel->GetNativeWindow());
    348   EXPECT_TRUE(toplevel_state->IsMinimized());
    349 
    350   toplevel->Restore();
    351   toplevel->GetNativeWindow()->SetBounds(bounds);
    352 
    353   // Check that swiping up doesn't maximize.
    354   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, -150);
    355   EXPECT_FALSE(toplevel_state->IsMaximized());
    356 
    357   toplevel->Restore();
    358   toplevel->GetNativeWindow()->SetBounds(bounds);
    359 
    360   // Check that swiping right doesn't snap.
    361   gfx::Rect normal_bounds = toplevel->GetWindowBoundsInScreen();
    362   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0);
    363   normal_bounds.set_x(normal_bounds.x() + 150);
    364   EXPECT_EQ(normal_bounds.ToString(),
    365       toplevel->GetWindowBoundsInScreen().ToString());
    366 
    367   toplevel->GetNativeWindow()->SetBounds(bounds);
    368 
    369   // Check that swiping left doesn't snap.
    370   normal_bounds = toplevel->GetWindowBoundsInScreen();
    371   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, -150, 0);
    372   normal_bounds.set_x(normal_bounds.x() - 150);
    373   EXPECT_EQ(normal_bounds.ToString(),
    374       toplevel->GetWindowBoundsInScreen().ToString());
    375 
    376   toplevel->GetNativeWindow()->SetBounds(bounds);
    377 
    378   // Swipe right again, make sure the window still doesn't snap.
    379   normal_bounds = toplevel->GetWindowBoundsInScreen();
    380   normal_bounds.set_x(normal_bounds.x() + 150);
    381   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0);
    382   EXPECT_EQ(normal_bounds.ToString(),
    383       toplevel->GetWindowBoundsInScreen().ToString());
    384 }
    385 
    386 TEST_F(SystemGestureEventFilterTest, DISABLED_TwoFingerDragEdge) {
    387   gfx::Rect bounds(0, 0, 200, 100);
    388   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    389   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    390       new ResizableWidgetDelegate, root_window, bounds);
    391   toplevel->Show();
    392 
    393   const int kSteps = 15;
    394   const int kTouchPoints = 2;
    395   gfx::Point points[kTouchPoints] = {
    396     gfx::Point(30, 20),  // Caption
    397     gfx::Point(0, 40),   // Left edge
    398   };
    399 
    400   EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()->
    401                       GetNonClientComponent(points[0]));
    402   EXPECT_EQ(HTLEFT, toplevel->GetNativeWindow()->delegate()->
    403         GetNonClientComponent(points[1]));
    404 
    405   aura::test::EventGenerator generator(root_window,
    406                                        toplevel->GetNativeWindow());
    407 
    408   bounds = toplevel->GetNativeWindow()->bounds();
    409   // Swipe down. Nothing should happen.
    410   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150);
    411   EXPECT_EQ(bounds.ToString(),
    412             toplevel->GetNativeWindow()->bounds().ToString());
    413 }
    414 
    415 // We do not allow resizing a window via multiple edges simultaneously. Test
    416 // that the behavior is reasonable if a user attempts to resize a window via
    417 // several edges.
    418 TEST_F(SystemGestureEventFilterTest,
    419        TwoFingerAttemptResizeLeftAndRightEdgesSimultaneously) {
    420   gfx::Rect initial_bounds(0, 0, 400, 400);
    421   views::Widget* toplevel =
    422       views::Widget::CreateWindowWithContextAndBounds(
    423           new ResizableWidgetDelegate, CurrentContext(), initial_bounds);
    424   toplevel->Show();
    425 
    426   const int kSteps = 15;
    427   const int kTouchPoints = 2;
    428   gfx::Point points[kTouchPoints] = {
    429     gfx::Point(0, 40),    // Left edge
    430     gfx::Point(399, 40),  // Right edge
    431   };
    432   int delays[kTouchPoints] = {0, 120};
    433 
    434   EXPECT_EQ(HTLEFT, toplevel->GetNonClientComponent(points[0]));
    435   EXPECT_EQ(HTRIGHT, toplevel->GetNonClientComponent(points[1]));
    436 
    437   GetEventGenerator().GestureMultiFingerScrollWithDelays(
    438       kTouchPoints, points, delays, 15, kSteps, 0, 40);
    439 
    440   // The window bounds should not have changed because neither of the fingers
    441   // moved horizontally.
    442   EXPECT_EQ(initial_bounds.ToString(),
    443             toplevel->GetNativeWindow()->bounds().ToString());
    444 }
    445 
    446 TEST_F(SystemGestureEventFilterTest, TwoFingerDragDelayed) {
    447   gfx::Rect bounds(0, 0, 200, 100);
    448   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    449   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    450       new ResizableWidgetDelegate, root_window, bounds);
    451   toplevel->Show();
    452 
    453   const int kSteps = 15;
    454   const int kTouchPoints = 2;
    455   gfx::Point points[kTouchPoints] = {
    456     gfx::Point(30, 20),  // Caption
    457     gfx::Point(34, 20),  // Caption
    458   };
    459   int delays[kTouchPoints] = {0, 120};
    460 
    461   EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()->
    462         GetNonClientComponent(points[0]));
    463   EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()->
    464         GetNonClientComponent(points[1]));
    465 
    466   aura::test::EventGenerator generator(root_window,
    467                                        toplevel->GetNativeWindow());
    468 
    469   bounds = toplevel->GetNativeWindow()->bounds();
    470   // Swipe right and down starting with one finger.
    471   // Add another finger after 120ms and continue dragging.
    472   // The window should move and the drag should be determined by the center
    473   // point between the fingers.
    474   generator.GestureMultiFingerScrollWithDelays(
    475       kTouchPoints, points, delays, 15, kSteps, 150, 150);
    476   bounds += gfx::Vector2d(150 + (points[1].x() - points[0].x()) / 2, 150);
    477   EXPECT_EQ(bounds.ToString(),
    478             toplevel->GetNativeWindow()->bounds().ToString());
    479 }
    480 
    481 TEST_F(SystemGestureEventFilterTest, ThreeFingerGestureStopsDrag) {
    482   gfx::Rect bounds(0, 0, 200, 100);
    483   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    484   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    485       new ResizableWidgetDelegate, root_window, bounds);
    486   toplevel->Show();
    487 
    488   const int kSteps = 10;
    489   const int kTouchPoints = 3;
    490   gfx::Point points[kTouchPoints] = {
    491     gfx::Point(30, 20),  // Caption
    492     gfx::Point(34, 20),  // Caption
    493     gfx::Point(38, 20),  // Caption
    494   };
    495   int delays[kTouchPoints] = {0, 0, 120};
    496 
    497   EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()->
    498         GetNonClientComponent(points[0]));
    499   EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()->
    500         GetNonClientComponent(points[1]));
    501 
    502   aura::test::EventGenerator generator(root_window,
    503                                        toplevel->GetNativeWindow());
    504 
    505   bounds = toplevel->GetNativeWindow()->bounds();
    506   // Swipe right and down starting with two fingers.
    507   // Add third finger after 120ms and continue dragging.
    508   // The window should start moving but stop when the 3rd finger touches down.
    509   const int kEventSeparation = 15;
    510   generator.GestureMultiFingerScrollWithDelays(
    511       kTouchPoints, points, delays, kEventSeparation, kSteps, 150, 150);
    512   int expected_drag = 150 / kSteps * 120 / kEventSeparation;
    513   bounds += gfx::Vector2d(expected_drag, expected_drag);
    514   EXPECT_EQ(bounds.ToString(),
    515             toplevel->GetNativeWindow()->bounds().ToString());
    516 }
    517 
    518 TEST_F(SystemGestureEventFilterTest, DragLeftNearEdgeSnaps) {
    519   gfx::Rect bounds(200, 150, 400, 100);
    520   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    521   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    522       new ResizableWidgetDelegate, root_window, bounds);
    523   toplevel->Show();
    524 
    525   const int kSteps = 15;
    526   const int kTouchPoints = 2;
    527   gfx::Point points[kTouchPoints] = {
    528     gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5),
    529     gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5),
    530   };
    531   aura::Window* toplevel_window = toplevel->GetNativeWindow();
    532   aura::test::EventGenerator generator(root_window, toplevel_window);
    533 
    534   // Check that dragging left snaps before reaching the screen edge.
    535   gfx::Rect work_area =
    536       Shell::GetScreen()->GetDisplayNearestWindow(root_window).work_area();
    537   int drag_x = work_area.x() + 20 - points[0].x();
    538   generator.GestureMultiFingerScroll(
    539       kTouchPoints, points, 120, kSteps, drag_x, 0);
    540 
    541   EXPECT_EQ(wm::GetDefaultLeftSnappedWindowBoundsInParent(
    542                 toplevel_window).ToString(),
    543             toplevel_window->bounds().ToString());
    544 }
    545 
    546 TEST_F(SystemGestureEventFilterTest, DragRightNearEdgeSnaps) {
    547   gfx::Rect bounds(200, 150, 400, 100);
    548   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    549   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    550       new ResizableWidgetDelegate, root_window, bounds);
    551   toplevel->Show();
    552 
    553   const int kSteps = 15;
    554   const int kTouchPoints = 2;
    555   gfx::Point points[kTouchPoints] = {
    556     gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5),
    557     gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5),
    558   };
    559   aura::Window* toplevel_window = toplevel->GetNativeWindow();
    560   aura::test::EventGenerator generator(root_window, toplevel_window);
    561 
    562   // Check that dragging right snaps before reaching the screen edge.
    563   gfx::Rect work_area =
    564       Shell::GetScreen()->GetDisplayNearestWindow(root_window).work_area();
    565   int drag_x = work_area.right() - 20 - points[0].x();
    566   generator.GestureMultiFingerScroll(
    567       kTouchPoints, points, 120, kSteps, drag_x, 0);
    568   EXPECT_EQ(wm::GetDefaultRightSnappedWindowBoundsInParent(
    569                 toplevel_window).ToString(),
    570             toplevel_window->bounds().ToString());
    571 }
    572 
    573 // Tests that the window manager does not consume gesture events targeted to
    574 // windows of type WINDOW_TYPE_CONTROL. This is important because the web
    575 // contents are often (but not always) of type WINDOW_TYPE_CONTROL.
    576 TEST_F(SystemGestureEventFilterTest,
    577        ControlWindowGetsMultiFingerGestureEvents) {
    578   scoped_ptr<aura::Window> parent(
    579       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100)));
    580 
    581   aura::test::EventCountDelegate delegate;
    582   delegate.set_window_component(HTCLIENT);
    583   scoped_ptr<aura::Window> child(new aura::Window(&delegate));
    584   child->SetType(ui::wm::WINDOW_TYPE_CONTROL);
    585   child->Init(aura::WINDOW_LAYER_TEXTURED);
    586   parent->AddChild(child.get());
    587   child->SetBounds(gfx::Rect(100, 100));
    588   child->Show();
    589 
    590   ui::test::TestEventHandler event_handler;
    591   aura::Env::GetInstance()->PrependPreTargetHandler(&event_handler);
    592 
    593   GetEventGenerator().MoveMouseTo(0, 0);
    594   for (int i = 1; i <= 3; ++i)
    595     GetEventGenerator().PressTouchId(i);
    596   for (int i = 1; i <= 3; ++i)
    597     GetEventGenerator().ReleaseTouchId(i);
    598   EXPECT_EQ(event_handler.num_gesture_events(),
    599             delegate.GetGestureCountAndReset());
    600 
    601   aura::Env::GetInstance()->RemovePreTargetHandler(&event_handler);
    602 }
    603 
    604 }  // namespace test
    605 }  // namespace ash
    606