Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / ash / wm / system_gesture_event_filter_unittest.cc
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