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/test_window_delegate.h"
     26 #include "ui/aura/test/test_windows.h"
     27 #include "ui/aura/window_event_dispatcher.h"
     28 #include "ui/base/hit_test.h"
     29 #include "ui/events/event.h"
     30 #include "ui/events/event_handler.h"
     31 #include "ui/events/event_utils.h"
     32 #include "ui/events/gestures/gesture_configuration.h"
     33 #include "ui/events/test/event_generator.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 bool CanMinimize() const OVERRIDE { return true; }
     56   virtual void DeleteDelegate() OVERRIDE { delete this; }
     57 
     58   DISALLOW_COPY_AND_ASSIGN(ResizableWidgetDelegate);
     59 };
     60 
     61 // Support class for testing windows with a maximum size.
     62 class MaxSizeNCFV : public views::NonClientFrameView {
     63  public:
     64   MaxSizeNCFV() {}
     65  private:
     66   virtual gfx::Size GetMaximumSize() const OVERRIDE {
     67     return gfx::Size(200, 200);
     68   }
     69   virtual gfx::Rect GetBoundsForClientView() const OVERRIDE {
     70     return gfx::Rect();
     71   };
     72 
     73   virtual gfx::Rect GetWindowBoundsForClientBounds(
     74       const gfx::Rect& client_bounds) const OVERRIDE {
     75     return gfx::Rect();
     76   };
     77 
     78   // This function must ask the ClientView to do a hittest.  We don't do this in
     79   // the parent NonClientView because that makes it more difficult to calculate
     80   // hittests for regions that are partially obscured by the ClientView, e.g.
     81   // HTSYSMENU.
     82   virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE {
     83     return HTNOWHERE;
     84   }
     85   virtual void GetWindowMask(const gfx::Size& size,
     86                              gfx::Path* window_mask) OVERRIDE {}
     87   virtual void ResetWindowControls() OVERRIDE {}
     88   virtual void UpdateWindowIcon() OVERRIDE {}
     89   virtual void UpdateWindowTitle() OVERRIDE {}
     90   virtual void SizeConstraintsChanged() OVERRIDE {}
     91 
     92   DISALLOW_COPY_AND_ASSIGN(MaxSizeNCFV);
     93 };
     94 
     95 class MaxSizeWidgetDelegate : public views::WidgetDelegateView {
     96  public:
     97   MaxSizeWidgetDelegate() {}
     98   virtual ~MaxSizeWidgetDelegate() {}
     99 
    100  private:
    101   virtual bool CanResize() const OVERRIDE { return true; }
    102   virtual bool CanMaximize() const OVERRIDE { return false; }
    103   virtual void DeleteDelegate() OVERRIDE { delete this; }
    104   virtual views::NonClientFrameView* CreateNonClientFrameView(
    105       views::Widget* widget) OVERRIDE {
    106     return new MaxSizeNCFV;
    107   }
    108 
    109   DISALLOW_COPY_AND_ASSIGN(MaxSizeWidgetDelegate);
    110 };
    111 
    112 } // namespace
    113 
    114 class SystemGestureEventFilterTest : public AshTestBase {
    115  public:
    116   SystemGestureEventFilterTest() : AshTestBase() {}
    117   virtual ~SystemGestureEventFilterTest() {}
    118 
    119   LongPressAffordanceHandler* GetLongPressAffordance() {
    120     ShellTestApi shell_test(Shell::GetInstance());
    121     return shell_test.system_gesture_event_filter()->
    122         long_press_affordance_.get();
    123   }
    124 
    125   base::OneShotTimer<LongPressAffordanceHandler>*
    126   GetLongPressAffordanceTimer() {
    127     return &GetLongPressAffordance()->timer_;
    128   }
    129 
    130   aura::Window* GetLongPressAffordanceTarget() {
    131     return GetLongPressAffordance()->tap_down_target_;
    132   }
    133 
    134   views::View* GetLongPressAffordanceView() {
    135     return reinterpret_cast<views::View*>(
    136         GetLongPressAffordance()->view_.get());
    137   }
    138 
    139   // Overridden from AshTestBase:
    140   virtual void SetUp() OVERRIDE {
    141     // TODO(jonross): TwoFingerDragDelayed() and ThreeFingerGestureStopsDrag()
    142     // both use hardcoded touch points, assuming that they target empty header
    143     // space. Window control order now reflects configuration files and can
    144     // change. The tests should be improved to dynamically decide touch points.
    145     // To address this we specify the originally expected window control
    146     // positions to be consistent across tests.
    147     std::vector<views::FrameButton> leading;
    148     std::vector<views::FrameButton> trailing;
    149     trailing.push_back(views::FRAME_BUTTON_MINIMIZE);
    150     trailing.push_back(views::FRAME_BUTTON_MAXIMIZE);
    151     trailing.push_back(views::FRAME_BUTTON_CLOSE);
    152     views::WindowButtonOrderProvider::GetInstance()->
    153         SetWindowButtonOrder(leading, trailing);
    154 
    155     test::AshTestBase::SetUp();
    156     // Enable brightness key.
    157     test::DisplayManagerTestApi(Shell::GetInstance()->display_manager()).
    158         SetFirstDisplayAsInternalDisplay();
    159   }
    160 
    161  private:
    162   DISALLOW_COPY_AND_ASSIGN(SystemGestureEventFilterTest);
    163 };
    164 
    165 ui::GestureEvent* CreateGesture(ui::EventType type,
    166                                 int x,
    167                                 int y,
    168                                 float delta_x,
    169                                 float delta_y,
    170                                 int touch_id) {
    171   ui::GestureEventDetails details =
    172       ui::GestureEventDetails(type, delta_x, delta_y);
    173   details.set_oldest_touch_id(touch_id);
    174   return new ui::GestureEvent(x, y, 0,
    175       base::TimeDelta::FromMilliseconds(base::Time::Now().ToDoubleT() * 1000),
    176       ui::GestureEventDetails(type, delta_x, delta_y));
    177 }
    178 
    179 TEST_F(SystemGestureEventFilterTest, LongPressAffordanceStateOnCaptureLoss) {
    180   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    181 
    182   aura::test::TestWindowDelegate delegate;
    183   scoped_ptr<aura::Window> window0(
    184       aura::test::CreateTestWindowWithDelegate(
    185           &delegate, 9, gfx::Rect(0, 0, 100, 100), root_window));
    186   scoped_ptr<aura::Window> window1(
    187       aura::test::CreateTestWindowWithDelegate(
    188           &delegate, 10, gfx::Rect(0, 0, 100, 50), window0.get()));
    189   scoped_ptr<aura::Window> window2(
    190       aura::test::CreateTestWindowWithDelegate(
    191           &delegate, 11, gfx::Rect(0, 50, 100, 50), window0.get()));
    192 
    193   const int kTouchId = 5;
    194 
    195   // Capture first window.
    196   window1->SetCapture();
    197   EXPECT_TRUE(window1->HasCapture());
    198 
    199   // Send touch event to first window.
    200   ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
    201                        gfx::Point(10, 10),
    202                        kTouchId,
    203                        ui::EventTimeForNow());
    204   ui::EventDispatchDetails details =
    205       root_window->GetHost()->dispatcher()->OnEventFromSource(&press);
    206   ASSERT_FALSE(details.dispatcher_destroyed);
    207   EXPECT_TRUE(window1->HasCapture());
    208 
    209   base::OneShotTimer<LongPressAffordanceHandler>* timer =
    210       GetLongPressAffordanceTimer();
    211   EXPECT_TRUE(timer->IsRunning());
    212   EXPECT_EQ(window1, GetLongPressAffordanceTarget());
    213 
    214   // Force timeout so that the affordance animation can start.
    215   timer->user_task().Run();
    216   timer->Stop();
    217   EXPECT_TRUE(GetLongPressAffordance()->is_animating());
    218 
    219   // Change capture.
    220   window2->SetCapture();
    221   EXPECT_TRUE(window2->HasCapture());
    222 
    223   EXPECT_TRUE(GetLongPressAffordance()->is_animating());
    224   EXPECT_EQ(window1, GetLongPressAffordanceTarget());
    225 
    226   // Animate to completion.
    227   GetLongPressAffordance()->End();  // end grow animation.
    228   // Force timeout to start shrink animation.
    229   EXPECT_TRUE(timer->IsRunning());
    230   timer->user_task().Run();
    231   timer->Stop();
    232   EXPECT_TRUE(GetLongPressAffordance()->is_animating());
    233   GetLongPressAffordance()->End();  // end shrink animation.
    234 
    235   // Check if state has reset.
    236   EXPECT_EQ(NULL, GetLongPressAffordanceTarget());
    237   EXPECT_EQ(NULL, GetLongPressAffordanceView());
    238 }
    239 
    240 TEST_F(SystemGestureEventFilterTest, TwoFingerDrag) {
    241   gfx::Rect bounds(0, 0, 600, 600);
    242   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    243   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    244       new ResizableWidgetDelegate, root_window, bounds);
    245   toplevel->Show();
    246 
    247   const int kSteps = 15;
    248   const int kTouchPoints = 2;
    249   gfx::Point points[kTouchPoints] = {
    250     gfx::Point(250, 250),
    251     gfx::Point(350, 350),
    252   };
    253 
    254   ui::test::EventGenerator generator(root_window, toplevel->GetNativeWindow());
    255 
    256   wm::WindowState* toplevel_state =
    257       wm::GetWindowState(toplevel->GetNativeWindow());
    258   // Swipe down to minimize.
    259   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150);
    260   EXPECT_TRUE(toplevel_state->IsMinimized());
    261 
    262   toplevel->Restore();
    263   toplevel->GetNativeWindow()->SetBounds(bounds);
    264 
    265   // Swipe up to maximize.
    266   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, -150);
    267   EXPECT_TRUE(toplevel_state->IsMaximized());
    268 
    269   toplevel->Restore();
    270   toplevel->GetNativeWindow()->SetBounds(bounds);
    271 
    272   // Swipe right to snap.
    273   gfx::Rect normal_bounds = toplevel->GetWindowBoundsInScreen();
    274   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0);
    275   gfx::Rect right_tile_bounds = toplevel->GetWindowBoundsInScreen();
    276   EXPECT_NE(normal_bounds.ToString(), right_tile_bounds.ToString());
    277 
    278   // Swipe left to snap.
    279   gfx::Point left_points[kTouchPoints];
    280   for (int i = 0; i < kTouchPoints; ++i) {
    281     left_points[i] = points[i];
    282     left_points[i].Offset(right_tile_bounds.x(), right_tile_bounds.y());
    283   }
    284   generator.GestureMultiFingerScroll(kTouchPoints, left_points, 15, kSteps,
    285       -150, 0);
    286   gfx::Rect left_tile_bounds = toplevel->GetWindowBoundsInScreen();
    287   EXPECT_NE(normal_bounds.ToString(), left_tile_bounds.ToString());
    288   EXPECT_NE(right_tile_bounds.ToString(), left_tile_bounds.ToString());
    289 
    290   // Swipe right again.
    291   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0);
    292   gfx::Rect current_bounds = toplevel->GetWindowBoundsInScreen();
    293   EXPECT_NE(current_bounds.ToString(), left_tile_bounds.ToString());
    294   EXPECT_EQ(current_bounds.ToString(), right_tile_bounds.ToString());
    295 }
    296 
    297 TEST_F(SystemGestureEventFilterTest, TwoFingerDragTwoWindows) {
    298   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    299   ui::GestureConfiguration::set_max_separation_for_gesture_touches_in_pixels(0);
    300   views::Widget* first = views::Widget::CreateWindowWithContextAndBounds(
    301       new ResizableWidgetDelegate, root_window, gfx::Rect(10, 0, 50, 100));
    302   first->Show();
    303   views::Widget* second = views::Widget::CreateWindowWithContextAndBounds(
    304       new ResizableWidgetDelegate, root_window, gfx::Rect(100, 0, 100, 100));
    305   second->Show();
    306 
    307   // Start a two-finger drag on |first|, and then try to use another two-finger
    308   // drag to move |second|. The attempt to move |second| should fail.
    309   const gfx::Rect& first_bounds = first->GetWindowBoundsInScreen();
    310   const gfx::Rect& second_bounds = second->GetWindowBoundsInScreen();
    311   const int kSteps = 15;
    312   const int kTouchPoints = 4;
    313   gfx::Point points[kTouchPoints] = {
    314     first_bounds.origin() + gfx::Vector2d(5, 5),
    315     first_bounds.origin() + gfx::Vector2d(30, 10),
    316     second_bounds.origin() + gfx::Vector2d(5, 5),
    317     second_bounds.origin() + gfx::Vector2d(40, 20)
    318   };
    319 
    320   ui::test::EventGenerator generator(root_window);
    321   // Do not drag too fast to avoid fling.
    322   generator.GestureMultiFingerScroll(kTouchPoints, points,
    323       50, kSteps, 0, 150);
    324 
    325   EXPECT_NE(first_bounds.ToString(),
    326             first->GetWindowBoundsInScreen().ToString());
    327   EXPECT_EQ(second_bounds.ToString(),
    328             second->GetWindowBoundsInScreen().ToString());
    329 }
    330 
    331 TEST_F(SystemGestureEventFilterTest, WindowsWithMaxSizeDontSnap) {
    332   gfx::Rect bounds(250, 150, 100, 100);
    333   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    334   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    335       new MaxSizeWidgetDelegate, root_window, bounds);
    336   toplevel->Show();
    337 
    338   const int kSteps = 15;
    339   const int kTouchPoints = 2;
    340   gfx::Point points[kTouchPoints] = {
    341     gfx::Point(bounds.x() + 10, bounds.y() + 30),
    342     gfx::Point(bounds.x() + 30, bounds.y() + 20),
    343   };
    344 
    345   ui::test::EventGenerator generator(root_window, toplevel->GetNativeWindow());
    346 
    347   // Swipe down to minimize.
    348   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150);
    349   wm::WindowState* toplevel_state =
    350       wm::GetWindowState(toplevel->GetNativeWindow());
    351   EXPECT_TRUE(toplevel_state->IsMinimized());
    352 
    353   toplevel->Restore();
    354   toplevel->GetNativeWindow()->SetBounds(bounds);
    355 
    356   // Check that swiping up doesn't maximize.
    357   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, -150);
    358   EXPECT_FALSE(toplevel_state->IsMaximized());
    359 
    360   toplevel->Restore();
    361   toplevel->GetNativeWindow()->SetBounds(bounds);
    362 
    363   // Check that swiping right doesn't snap.
    364   gfx::Rect normal_bounds = toplevel->GetWindowBoundsInScreen();
    365   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0);
    366   normal_bounds.set_x(normal_bounds.x() + 150);
    367   EXPECT_EQ(normal_bounds.ToString(),
    368       toplevel->GetWindowBoundsInScreen().ToString());
    369 
    370   toplevel->GetNativeWindow()->SetBounds(bounds);
    371 
    372   // Check that swiping left doesn't snap.
    373   normal_bounds = toplevel->GetWindowBoundsInScreen();
    374   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, -150, 0);
    375   normal_bounds.set_x(normal_bounds.x() - 150);
    376   EXPECT_EQ(normal_bounds.ToString(),
    377       toplevel->GetWindowBoundsInScreen().ToString());
    378 
    379   toplevel->GetNativeWindow()->SetBounds(bounds);
    380 
    381   // Swipe right again, make sure the window still doesn't snap.
    382   normal_bounds = toplevel->GetWindowBoundsInScreen();
    383   normal_bounds.set_x(normal_bounds.x() + 150);
    384   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0);
    385   EXPECT_EQ(normal_bounds.ToString(),
    386       toplevel->GetWindowBoundsInScreen().ToString());
    387 }
    388 
    389 TEST_F(SystemGestureEventFilterTest, DISABLED_TwoFingerDragEdge) {
    390   gfx::Rect bounds(0, 0, 200, 100);
    391   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    392   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    393       new ResizableWidgetDelegate, root_window, bounds);
    394   toplevel->Show();
    395 
    396   const int kSteps = 15;
    397   const int kTouchPoints = 2;
    398   gfx::Point points[kTouchPoints] = {
    399     gfx::Point(30, 20),  // Caption
    400     gfx::Point(0, 40),   // Left edge
    401   };
    402 
    403   EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()->
    404                       GetNonClientComponent(points[0]));
    405   EXPECT_EQ(HTLEFT, toplevel->GetNativeWindow()->delegate()->
    406         GetNonClientComponent(points[1]));
    407 
    408   ui::test::EventGenerator generator(root_window, toplevel->GetNativeWindow());
    409 
    410   bounds = toplevel->GetNativeWindow()->bounds();
    411   // Swipe down. Nothing should happen.
    412   generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150);
    413   EXPECT_EQ(bounds.ToString(),
    414             toplevel->GetNativeWindow()->bounds().ToString());
    415 }
    416 
    417 // We do not allow resizing a window via multiple edges simultaneously. Test
    418 // that the behavior is reasonable if a user attempts to resize a window via
    419 // several edges.
    420 TEST_F(SystemGestureEventFilterTest,
    421        TwoFingerAttemptResizeLeftAndRightEdgesSimultaneously) {
    422   gfx::Rect initial_bounds(0, 0, 400, 400);
    423   views::Widget* toplevel =
    424       views::Widget::CreateWindowWithContextAndBounds(
    425           new ResizableWidgetDelegate, CurrentContext(), initial_bounds);
    426   toplevel->Show();
    427 
    428   const int kSteps = 15;
    429   const int kTouchPoints = 2;
    430   gfx::Point points[kTouchPoints] = {
    431     gfx::Point(0, 40),    // Left edge
    432     gfx::Point(399, 40),  // Right edge
    433   };
    434   int delays[kTouchPoints] = {0, 120};
    435 
    436   EXPECT_EQ(HTLEFT, toplevel->GetNonClientComponent(points[0]));
    437   EXPECT_EQ(HTRIGHT, toplevel->GetNonClientComponent(points[1]));
    438 
    439   GetEventGenerator().GestureMultiFingerScrollWithDelays(
    440       kTouchPoints, points, delays, 15, kSteps, 0, 40);
    441 
    442   // The window bounds should not have changed because neither of the fingers
    443   // moved horizontally.
    444   EXPECT_EQ(initial_bounds.ToString(),
    445             toplevel->GetNativeWindow()->bounds().ToString());
    446 }
    447 
    448 TEST_F(SystemGestureEventFilterTest, TwoFingerDragDelayed) {
    449   gfx::Rect bounds(0, 0, 200, 100);
    450   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    451   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    452       new ResizableWidgetDelegate, root_window, bounds);
    453   toplevel->Show();
    454 
    455   const int kSteps = 15;
    456   const int kTouchPoints = 2;
    457   gfx::Point points[kTouchPoints] = {
    458     gfx::Point(30, 20),  // Caption
    459     gfx::Point(34, 20),  // Caption
    460   };
    461   int delays[kTouchPoints] = {0, 120};
    462 
    463   EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()->
    464         GetNonClientComponent(points[0]));
    465   EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()->
    466         GetNonClientComponent(points[1]));
    467 
    468   ui::test::EventGenerator generator(root_window, toplevel->GetNativeWindow());
    469 
    470   bounds = toplevel->GetNativeWindow()->bounds();
    471   // Swipe right and down starting with one finger.
    472   // Add another finger after 120ms and continue dragging.
    473   // The window should move and the drag should be determined by the center
    474   // point between the fingers.
    475   generator.GestureMultiFingerScrollWithDelays(
    476       kTouchPoints, points, delays, 15, kSteps, 150, 150);
    477   bounds += gfx::Vector2d(150 + (points[1].x() - points[0].x()) / 2, 150);
    478   EXPECT_EQ(bounds.ToString(),
    479             toplevel->GetNativeWindow()->bounds().ToString());
    480 }
    481 
    482 TEST_F(SystemGestureEventFilterTest, ThreeFingerGestureStopsDrag) {
    483   gfx::Rect bounds(0, 0, 200, 100);
    484   aura::Window* root_window = Shell::GetPrimaryRootWindow();
    485   views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds(
    486       new ResizableWidgetDelegate, root_window, bounds);
    487   toplevel->Show();
    488 
    489   const int kSteps = 10;
    490   const int kTouchPoints = 3;
    491   gfx::Point points[kTouchPoints] = {
    492     gfx::Point(30, 20),  // Caption
    493     gfx::Point(34, 20),  // Caption
    494     gfx::Point(38, 20),  // Caption
    495   };
    496   int delays[kTouchPoints] = {0, 0, 120};
    497 
    498   EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()->
    499         GetNonClientComponent(points[0]));
    500   EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()->
    501         GetNonClientComponent(points[1]));
    502 
    503   ui::test::EventGenerator generator(root_window, 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   ui::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   ui::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