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