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