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/toplevel_window_event_handler.h"
6
7#include "ash/ash_constants.h"
8#include "ash/root_window_controller.h"
9#include "ash/shell.h"
10#include "ash/shell_window_ids.h"
11#include "ash/test/ash_test_base.h"
12#include "ash/wm/lock_state_controller_impl2.h"
13#include "ash/wm/property_util.h"
14#include "ash/wm/resize_shadow.h"
15#include "ash/wm/resize_shadow_controller.h"
16#include "ash/wm/window_util.h"
17#include "ash/wm/workspace/snap_sizer.h"
18#include "ash/wm/workspace_controller.h"
19#include "base/basictypes.h"
20#include "base/compiler_specific.h"
21#include "testing/gtest/include/gtest/gtest.h"
22#include "ui/aura/client/aura_constants.h"
23#include "ui/aura/root_window.h"
24#include "ui/aura/test/aura_test_base.h"
25#include "ui/aura/test/event_generator.h"
26#include "ui/aura/test/test_activation_client.h"
27#include "ui/aura/test/test_window_delegate.h"
28#include "ui/base/events/event.h"
29#include "ui/base/hit_test.h"
30#include "ui/gfx/screen.h"
31
32#if defined(OS_WIN)
33// Windows headers define macros for these function names which screw with us.
34#if defined(CreateWindow)
35#undef CreateWindow
36#endif
37#endif
38
39namespace ash {
40namespace test {
41
42namespace {
43
44// A simple window delegate that returns the specified hit-test code when
45// requested and applies a minimum size constraint if there is one.
46class TestWindowDelegate : public aura::test::TestWindowDelegate {
47 public:
48  explicit TestWindowDelegate(int hittest_code) {
49    set_window_component(hittest_code);
50  }
51  virtual ~TestWindowDelegate() {}
52
53 private:
54  // Overridden from aura::Test::TestWindowDelegate:
55  virtual void OnWindowDestroyed() OVERRIDE {
56    delete this;
57  }
58
59  DISALLOW_COPY_AND_ASSIGN(TestWindowDelegate);
60};
61
62class ToplevelWindowEventHandlerTest : public AshTestBase {
63 public:
64  ToplevelWindowEventHandlerTest() {}
65  virtual ~ToplevelWindowEventHandlerTest() {}
66
67 protected:
68  aura::Window* CreateWindow(int hittest_code) {
69    TestWindowDelegate* d1 = new TestWindowDelegate(hittest_code);
70    aura::Window* w1 = new aura::Window(d1);
71    w1->SetType(aura::client::WINDOW_TYPE_NORMAL);
72    w1->set_id(1);
73    w1->Init(ui::LAYER_TEXTURED);
74    aura::Window* parent =
75      Shell::GetContainer(Shell::GetPrimaryRootWindow(),
76                          internal::kShellWindowId_AlwaysOnTopContainer);
77    parent->AddChild(w1);
78    w1->SetBounds(gfx::Rect(0, 0, 100, 100));
79    w1->Show();
80    return w1;
81  }
82
83  void DragFromCenterBy(aura::Window* window, int dx, int dy) {
84    aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), window);
85    generator.DragMouseBy(dx, dy);
86  }
87
88  void TouchDragFromCenterBy(aura::Window* window, int dx, int dy) {
89    aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), window);
90    generator.PressMoveAndReleaseTouchBy(dx, dy);
91  }
92
93  scoped_ptr<ToplevelWindowEventHandler> handler_;
94
95 private:
96  DISALLOW_COPY_AND_ASSIGN(ToplevelWindowEventHandlerTest);
97};
98
99}
100
101TEST_F(ToplevelWindowEventHandlerTest, Caption) {
102  scoped_ptr<aura::Window> w1(CreateWindow(HTCAPTION));
103  gfx::Size size = w1->bounds().size();
104  DragFromCenterBy(w1.get(), 100, 100);
105  // Position should have been offset by 100,100.
106  EXPECT_EQ("100,100", w1->bounds().origin().ToString());
107  // Size should not have.
108  EXPECT_EQ(size.ToString(), w1->bounds().size().ToString());
109
110  TouchDragFromCenterBy(w1.get(), 100, 100);
111  // Position should have been offset by 100,100.
112  EXPECT_EQ("200,200", w1->bounds().origin().ToString());
113  // Size should not have.
114  EXPECT_EQ(size.ToString(), w1->bounds().size().ToString());
115}
116
117TEST_F(ToplevelWindowEventHandlerTest, BottomRight) {
118  scoped_ptr<aura::Window> w1(CreateWindow(HTBOTTOMRIGHT));
119  gfx::Point position = w1->bounds().origin();
120  DragFromCenterBy(w1.get(), 100, 100);
121  // Position should not have changed.
122  EXPECT_EQ(position, w1->bounds().origin());
123  // Size should have increased by 100,100.
124  EXPECT_EQ(gfx::Size(200, 200), w1->bounds().size());
125}
126
127TEST_F(ToplevelWindowEventHandlerTest, GrowBox) {
128  scoped_ptr<aura::Window> w1(CreateWindow(HTGROWBOX));
129  TestWindowDelegate* window_delegate =
130      static_cast<TestWindowDelegate*>(w1->delegate());
131  window_delegate->set_minimum_size(gfx::Size(40, 40));
132
133  gfx::Point position = w1->bounds().origin();
134  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
135  generator.MoveMouseToCenterOf(w1.get());
136  generator.DragMouseBy(100, 100);
137  // Position should not have changed.
138  EXPECT_EQ(position, w1->bounds().origin());
139  // Size should have increased by 100,100.
140  EXPECT_EQ(gfx::Size(200, 200), w1->bounds().size());
141
142  // Shrink the wnidow by (-100, -100).
143  generator.DragMouseBy(-100, -100);
144  // Position should not have changed.
145  EXPECT_EQ(position, w1->bounds().origin());
146  // Size should have decreased by 100,100.
147  EXPECT_EQ(gfx::Size(100, 100), w1->bounds().size());
148
149  // Enforce minimum size.
150  generator.DragMouseBy(-60, -60);
151  EXPECT_EQ(position, w1->bounds().origin());
152  EXPECT_EQ(gfx::Size(40, 40), w1->bounds().size());
153}
154
155TEST_F(ToplevelWindowEventHandlerTest, Right) {
156  scoped_ptr<aura::Window> w1(CreateWindow(HTRIGHT));
157  gfx::Point position = w1->bounds().origin();
158  DragFromCenterBy(w1.get(), 100, 100);
159  // Position should not have changed.
160  EXPECT_EQ(position, w1->bounds().origin());
161  // Size should have increased by 100,0.
162  EXPECT_EQ(gfx::Size(200, 100), w1->bounds().size());
163}
164
165TEST_F(ToplevelWindowEventHandlerTest, Bottom) {
166  scoped_ptr<aura::Window> w1(CreateWindow(HTBOTTOM));
167  gfx::Point position = w1->bounds().origin();
168  DragFromCenterBy(w1.get(), 100, 100);
169  // Position should not have changed.
170  EXPECT_EQ(position, w1->bounds().origin());
171  // Size should have increased by 0,100.
172  EXPECT_EQ(gfx::Size(100, 200), w1->bounds().size());
173}
174
175TEST_F(ToplevelWindowEventHandlerTest, TopRight) {
176  scoped_ptr<aura::Window> w1(CreateWindow(HTTOPRIGHT));
177  DragFromCenterBy(w1.get(), -50, 50);
178  // Position should have been offset by 0,50.
179  EXPECT_EQ(gfx::Point(0, 50), w1->bounds().origin());
180  // Size should have decreased by 50,50.
181  EXPECT_EQ(gfx::Size(50, 50), w1->bounds().size());
182}
183
184TEST_F(ToplevelWindowEventHandlerTest, Top) {
185  scoped_ptr<aura::Window> w1(CreateWindow(HTTOP));
186  DragFromCenterBy(w1.get(), 50, 50);
187  // Position should have been offset by 0,50.
188  EXPECT_EQ(gfx::Point(0, 50), w1->bounds().origin());
189  // Size should have decreased by 0,50.
190  EXPECT_EQ(gfx::Size(100, 50), w1->bounds().size());
191}
192
193TEST_F(ToplevelWindowEventHandlerTest, Left) {
194  scoped_ptr<aura::Window> w1(CreateWindow(HTLEFT));
195  DragFromCenterBy(w1.get(), 50, 50);
196  // Position should have been offset by 50,0.
197  EXPECT_EQ(gfx::Point(50, 0), w1->bounds().origin());
198  // Size should have decreased by 50,0.
199  EXPECT_EQ(gfx::Size(50, 100), w1->bounds().size());
200}
201
202TEST_F(ToplevelWindowEventHandlerTest, BottomLeft) {
203  scoped_ptr<aura::Window> w1(CreateWindow(HTBOTTOMLEFT));
204  DragFromCenterBy(w1.get(), 50, -50);
205  // Position should have been offset by 50,0.
206  EXPECT_EQ(gfx::Point(50, 0), w1->bounds().origin());
207  // Size should have decreased by 50,50.
208  EXPECT_EQ(gfx::Size(50, 50), w1->bounds().size());
209}
210
211TEST_F(ToplevelWindowEventHandlerTest, TopLeft) {
212  scoped_ptr<aura::Window> w1(CreateWindow(HTTOPLEFT));
213  DragFromCenterBy(w1.get(), 50, 50);
214  // Position should have been offset by 50,50.
215  EXPECT_EQ(gfx::Point(50, 50), w1->bounds().origin());
216  // Size should have decreased by 50,50.
217  EXPECT_EQ(gfx::Size(50, 50), w1->bounds().size());
218}
219
220TEST_F(ToplevelWindowEventHandlerTest, Client) {
221  scoped_ptr<aura::Window> w1(CreateWindow(HTCLIENT));
222  gfx::Rect bounds = w1->bounds();
223  DragFromCenterBy(w1.get(), 100, 100);
224  // Neither position nor size should have changed.
225  EXPECT_EQ(bounds, w1->bounds());
226}
227
228TEST_F(ToplevelWindowEventHandlerTest, LeftPastMinimum) {
229  scoped_ptr<aura::Window> w1(CreateWindow(HTLEFT));
230  TestWindowDelegate* window_delegate =
231      static_cast<TestWindowDelegate*>(w1->delegate());
232  window_delegate->set_minimum_size(gfx::Size(40, 40));
233
234  // Simulate a large left-to-right drag.  Window width should be clamped to
235  // minimum and position change should be limited as well.
236  DragFromCenterBy(w1.get(), 333, 0);
237  EXPECT_EQ(gfx::Point(60, 0), w1->bounds().origin());
238  EXPECT_EQ(gfx::Size(40, 100), w1->bounds().size());
239}
240
241TEST_F(ToplevelWindowEventHandlerTest, RightPastMinimum) {
242  scoped_ptr<aura::Window> w1(CreateWindow(HTRIGHT));
243  TestWindowDelegate* window_delegate =
244      static_cast<TestWindowDelegate*>(w1->delegate());
245  window_delegate->set_minimum_size(gfx::Size(40, 40));
246  gfx::Point position = w1->bounds().origin();
247
248  // Simulate a large right-to-left drag.  Window width should be clamped to
249  // minimum and position should not change.
250  DragFromCenterBy(w1.get(), -333, 0);
251  EXPECT_EQ(position, w1->bounds().origin());
252  EXPECT_EQ(gfx::Size(40, 100), w1->bounds().size());
253}
254
255TEST_F(ToplevelWindowEventHandlerTest, TopLeftPastMinimum) {
256  scoped_ptr<aura::Window> w1(CreateWindow(HTTOPLEFT));
257  TestWindowDelegate* window_delegate =
258      static_cast<TestWindowDelegate*>(w1->delegate());
259  window_delegate->set_minimum_size(gfx::Size(40, 40));
260
261  // Simulate a large top-left to bottom-right drag.  Window width should be
262  // clamped to minimum and position should be limited.
263  DragFromCenterBy(w1.get(), 333, 444);
264  EXPECT_EQ(gfx::Point(60, 60), w1->bounds().origin());
265  EXPECT_EQ(gfx::Size(40, 40), w1->bounds().size());
266}
267
268TEST_F(ToplevelWindowEventHandlerTest, TopRightPastMinimum) {
269  scoped_ptr<aura::Window> w1(CreateWindow(HTTOPRIGHT));
270  TestWindowDelegate* window_delegate =
271      static_cast<TestWindowDelegate*>(w1->delegate());
272  window_delegate->set_minimum_size(gfx::Size(40, 40));
273
274  // Simulate a large top-right to bottom-left drag.  Window size should be
275  // clamped to minimum, x position should not change, and y position should
276  // be clamped.
277  DragFromCenterBy(w1.get(), -333, 444);
278  EXPECT_EQ(gfx::Point(0, 60), w1->bounds().origin());
279  EXPECT_EQ(gfx::Size(40, 40), w1->bounds().size());
280}
281
282TEST_F(ToplevelWindowEventHandlerTest, BottomLeftPastMinimum) {
283  scoped_ptr<aura::Window> w1(CreateWindow(HTBOTTOMLEFT));
284  TestWindowDelegate* window_delegate =
285      static_cast<TestWindowDelegate*>(w1->delegate());
286  window_delegate->set_minimum_size(gfx::Size(40, 40));
287
288  // Simulate a large bottom-left to top-right drag.  Window size should be
289  // clamped to minimum, x position should be clamped, and y position should
290  // not change.
291  DragFromCenterBy(w1.get(), 333, -444);
292  EXPECT_EQ(gfx::Point(60, 0), w1->bounds().origin());
293  EXPECT_EQ(gfx::Size(40, 40), w1->bounds().size());
294}
295
296TEST_F(ToplevelWindowEventHandlerTest, BottomRightPastMinimum) {
297  scoped_ptr<aura::Window> w1(CreateWindow(HTBOTTOMRIGHT));
298  TestWindowDelegate* window_delegate =
299      static_cast<TestWindowDelegate*>(w1->delegate());
300  window_delegate->set_minimum_size(gfx::Size(40, 40));
301  gfx::Point position = w1->bounds().origin();
302
303  // Simulate a large bottom-right to top-left drag.  Window size should be
304  // clamped to minimum and position should not change.
305  DragFromCenterBy(w1.get(), -333, -444);
306  EXPECT_EQ(position, w1->bounds().origin());
307  EXPECT_EQ(gfx::Size(40, 40), w1->bounds().size());
308}
309
310TEST_F(ToplevelWindowEventHandlerTest, BottomRightWorkArea) {
311  scoped_ptr<aura::Window> target(CreateWindow(HTBOTTOMRIGHT));
312  gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(
313      target.get()).work_area();
314  gfx::Point position = target->bounds().origin();
315  // Drag further than work_area bottom.
316  DragFromCenterBy(target.get(), 100, work_area.height());
317  // Position should not have changed.
318  EXPECT_EQ(position, target->bounds().origin());
319  // Size should have increased by 100, work_area.height() - target->bounds.y()
320  EXPECT_EQ(gfx::Size(200, work_area.height() - target->bounds().y()),
321            target->bounds().size());
322}
323
324TEST_F(ToplevelWindowEventHandlerTest, BottomLeftWorkArea) {
325  scoped_ptr<aura::Window> target(CreateWindow(HTBOTTOMLEFT));
326  gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(
327      target.get()).work_area();
328  gfx::Point position = target->bounds().origin();
329  // Drag further than work_area bottom.
330  DragFromCenterBy(target.get(), -30, work_area.height());
331  // origin is now at 70, 100.
332  EXPECT_EQ(position.x() - 30, target->bounds().x());
333  EXPECT_EQ(position.y(), target->bounds().y());
334  // Size should have increased by 30, work_area.height() - target->bounds.y()
335  EXPECT_EQ(gfx::Size(130, work_area.height() - target->bounds().y()),
336            target->bounds().size());
337}
338
339TEST_F(ToplevelWindowEventHandlerTest, BottomWorkArea) {
340  scoped_ptr<aura::Window> target(CreateWindow(HTBOTTOM));
341  gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(
342      target.get()).work_area();
343  gfx::Point position = target->bounds().origin();
344  // Drag further than work_area bottom.
345  DragFromCenterBy(target.get(), 0, work_area.height());
346  // Position should not have changed.
347  EXPECT_EQ(position, target->bounds().origin());
348  // Size should have increased by 0, work_area.height() - target->bounds.y()
349  EXPECT_EQ(gfx::Size(100, work_area.height() - target->bounds().y()),
350            target->bounds().size());
351}
352
353// Verifies we don't let windows drag to a -y location.
354TEST_F(ToplevelWindowEventHandlerTest, DontDragToNegativeY) {
355  scoped_ptr<aura::Window> target(CreateWindow(HTTOP));
356  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
357                                       target.get());
358  generator.MoveMouseTo(0, 5);
359  generator.DragMouseBy(0, -5);
360  // The y location and height should not have changed.
361  EXPECT_EQ(0, target->bounds().y());
362  EXPECT_EQ(100, target->bounds().height());
363}
364
365// Verifies we don't let windows go bigger than the display width.
366TEST_F(ToplevelWindowEventHandlerTest, DontGotWiderThanScreen) {
367  scoped_ptr<aura::Window> target(CreateWindow(HTRIGHT));
368  gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(
369      target.get()).bounds();
370  DragFromCenterBy(target.get(), work_area.width() * 2, 0);
371  // The y location and height should not have changed.
372  EXPECT_EQ(work_area.width(), target->bounds().width());
373}
374
375// Verifies that touch-gestures drag the window correctly.
376TEST_F(ToplevelWindowEventHandlerTest, GestureDrag) {
377  scoped_ptr<aura::Window> target(
378      CreateTestWindowInShellWithDelegate(
379          new TestWindowDelegate(HTCAPTION),
380          0,
381          gfx::Rect(0, 0, 100, 100)));
382  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
383                                       target.get());
384  gfx::Rect old_bounds = target->bounds();
385  gfx::Point location(5, 5);
386  target->SetProperty(aura::client::kCanMaximizeKey, true);
387
388  gfx::Point end = location;
389
390  // Snap right;
391  {
392    // Get the expected snapped bounds before snapping.
393    internal::SnapSizer sizer(target.get(), location,
394        internal::SnapSizer::RIGHT_EDGE,
395        internal::SnapSizer::OTHER_INPUT);
396    gfx::Rect snapped_bounds = sizer.GetSnapBounds(target->bounds());
397
398    end.Offset(100, 0);
399    generator.GestureScrollSequence(location, end,
400        base::TimeDelta::FromMilliseconds(5),
401        10);
402    RunAllPendingInMessageLoop();
403
404    // Verify that the window has moved after the gesture.
405    EXPECT_NE(old_bounds.ToString(), target->bounds().ToString());
406    EXPECT_EQ(snapped_bounds.ToString(), target->bounds().ToString());
407  }
408
409  old_bounds = target->bounds();
410
411  // Snap left.
412  {
413    // Get the expected snapped bounds before snapping.
414    internal::SnapSizer sizer(target.get(), location,
415        internal::SnapSizer::LEFT_EDGE,
416        internal::SnapSizer::OTHER_INPUT);
417    gfx::Rect snapped_bounds = sizer.GetSnapBounds(target->bounds());
418    end = location = target->GetBoundsInRootWindow().CenterPoint();
419    end.Offset(-100, 0);
420    generator.GestureScrollSequence(location, end,
421        base::TimeDelta::FromMilliseconds(5),
422        10);
423    RunAllPendingInMessageLoop();
424
425    EXPECT_NE(old_bounds.ToString(), target->bounds().ToString());
426    EXPECT_EQ(snapped_bounds.ToString(), target->bounds().ToString());
427  }
428
429  gfx::Rect bounds_before_maximization = target->bounds();
430  bounds_before_maximization.Offset(0, 100);
431  target->SetBounds(bounds_before_maximization);
432  old_bounds = target->bounds();
433
434  // Maximize.
435  end = location = target->GetBoundsInRootWindow().CenterPoint();
436  end.Offset(0, -100);
437  generator.GestureScrollSequence(location, end,
438      base::TimeDelta::FromMilliseconds(5),
439      10);
440  RunAllPendingInMessageLoop();
441  EXPECT_NE(old_bounds.ToString(), target->bounds().ToString());
442  EXPECT_TRUE(wm::IsWindowMaximized(target.get()));
443  EXPECT_EQ(old_bounds.ToString(),
444            GetRestoreBoundsInScreen(target.get())->ToString());
445
446  wm::RestoreWindow(target.get());
447  target->SetBounds(old_bounds);
448
449  // Minimize.
450  end = location = target->GetBoundsInRootWindow().CenterPoint();
451  end.Offset(0, 100);
452  generator.GestureScrollSequence(location, end,
453      base::TimeDelta::FromMilliseconds(5),
454      10);
455  RunAllPendingInMessageLoop();
456  EXPECT_NE(old_bounds.ToString(), target->bounds().ToString());
457  EXPECT_TRUE(wm::IsWindowMinimized(target.get()));
458  EXPECT_TRUE(GetWindowAlwaysRestoresToRestoreBounds(target.get()));
459  EXPECT_EQ(old_bounds.ToString(),
460            GetRestoreBoundsInScreen(target.get())->ToString());
461}
462
463// Tests that a gesture cannot minimize a window in login/lock screen.
464TEST_F(ToplevelWindowEventHandlerTest, GestureDragMinimizeLoginScreen) {
465  LockStateControllerImpl2* state_controller =
466      static_cast<LockStateControllerImpl2*>
467          (Shell::GetInstance()->lock_state_controller());
468  state_controller->OnLoginStateChanged(user::LOGGED_IN_NONE);
469  state_controller->OnLockStateChanged(false);
470  SetUserLoggedIn(false);
471
472  scoped_ptr<aura::Window> target(CreateWindow(HTCAPTION));
473  aura::Window* lock = internal::RootWindowController::ForWindow(target.get())->
474      GetContainer(internal::kShellWindowId_LockSystemModalContainer);
475  lock->AddChild(target.get());
476  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
477                                       target.get());
478  gfx::Rect old_bounds = target->bounds();
479  gfx::Point location(5, 5);
480  target->SetProperty(aura::client::kCanMaximizeKey, true);
481
482  gfx::Point end = location;
483  end.Offset(0, 100);
484  generator.GestureScrollSequence(location, end,
485      base::TimeDelta::FromMilliseconds(5),
486      10);
487  RunAllPendingInMessageLoop();
488  EXPECT_FALSE(wm::IsWindowMinimized(target.get()));
489}
490
491TEST_F(ToplevelWindowEventHandlerTest, GestureDragToRestore) {
492  scoped_ptr<aura::Window> window(
493      CreateTestWindowInShellWithDelegate(
494          new TestWindowDelegate(HTCAPTION),
495          0,
496          gfx::Rect(10, 20, 30, 40)));
497  window->Show();
498  ash::wm::ActivateWindow(window.get());
499
500  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
501                                       window.get());
502  gfx::Rect old_bounds = window->bounds();
503  gfx::Point location, end;
504  end = location = window->GetBoundsInRootWindow().CenterPoint();
505  end.Offset(0, 100);
506  generator.GestureScrollSequence(location, end,
507      base::TimeDelta::FromMilliseconds(5),
508      10);
509  RunAllPendingInMessageLoop();
510  EXPECT_NE(old_bounds.ToString(), window->bounds().ToString());
511  EXPECT_TRUE(wm::IsWindowMinimized(window.get()));
512  EXPECT_TRUE(GetWindowAlwaysRestoresToRestoreBounds(window.get()));
513  EXPECT_EQ(old_bounds.ToString(),
514            GetRestoreBoundsInScreen(window.get())->ToString());
515}
516
517// Tests that an unresizable window cannot be dragged or snapped using gestures.
518TEST_F(ToplevelWindowEventHandlerTest, GestureDragForUnresizableWindow) {
519  scoped_ptr<aura::Window> target(CreateWindow(HTCAPTION));
520
521  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
522                                       target.get());
523  gfx::Rect old_bounds = target->bounds();
524  gfx::Point location(5, 5);
525
526  target->SetProperty(aura::client::kCanResizeKey, false);
527
528  gfx::Point end = location;
529
530  // Try to snap right. The window is not resizable. So it should not snap.
531  {
532    // Get the expected snapped bounds before the gesture.
533    internal::SnapSizer sizer(target.get(), location,
534        internal::SnapSizer::RIGHT_EDGE,
535        internal::SnapSizer::OTHER_INPUT);
536    gfx::Rect snapped_bounds = sizer.GetSnapBounds(target->bounds());
537
538    end.Offset(100, 0);
539    generator.GestureScrollSequence(location, end,
540        base::TimeDelta::FromMilliseconds(5),
541        10);
542    RunAllPendingInMessageLoop();
543
544    // Verify that the window has moved after the gesture.
545    gfx::Rect expected_bounds(old_bounds);
546    expected_bounds.Offset(gfx::Vector2d(100, 0));
547    EXPECT_EQ(expected_bounds.ToString(), target->bounds().ToString());
548
549    // Verify that the window did not snap left.
550    EXPECT_NE(snapped_bounds.ToString(), target->bounds().ToString());
551  }
552
553  old_bounds = target->bounds();
554
555  // Try to snap left. It should not snap.
556  {
557    // Get the expected snapped bounds before the gesture.
558    internal::SnapSizer sizer(target.get(), location,
559        internal::SnapSizer::LEFT_EDGE,
560        internal::SnapSizer::OTHER_INPUT);
561    gfx::Rect snapped_bounds = sizer.GetSnapBounds(target->bounds());
562    end = location = target->GetBoundsInRootWindow().CenterPoint();
563    end.Offset(-100, 0);
564    generator.GestureScrollSequence(location, end,
565        base::TimeDelta::FromMilliseconds(5),
566        10);
567    RunAllPendingInMessageLoop();
568
569    // Verify that the window has moved after the gesture.
570    gfx::Rect expected_bounds(old_bounds);
571    expected_bounds.Offset(gfx::Vector2d(-100, 0));
572    EXPECT_EQ(expected_bounds.ToString(), target->bounds().ToString());
573
574    // Verify that the window did not snap left.
575    EXPECT_NE(snapped_bounds.ToString(), target->bounds().ToString());
576  }
577}
578
579// Tests that dragging multiple windows at the same time is not allowed.
580TEST_F(ToplevelWindowEventHandlerTest, GestureDragMultipleWindows) {
581  scoped_ptr<aura::Window> target(
582      CreateTestWindowInShellWithDelegate(
583          new TestWindowDelegate(HTCAPTION),
584          0,
585          gfx::Rect(0, 0, 100, 100)));
586  scoped_ptr<aura::Window> notmoved(
587      CreateTestWindowInShellWithDelegate(
588          new TestWindowDelegate(HTCAPTION),
589          1, gfx::Rect(100, 0, 100, 100)));
590
591  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
592                                       target.get());
593  gfx::Rect old_bounds = target->bounds();
594  gfx::Point location(5, 5);
595  target->SetProperty(aura::client::kCanMaximizeKey, true);
596
597  // Send some touch events to start dragging |target|.
598  generator.MoveTouch(location);
599  generator.PressTouch();
600  location.Offset(40, 5);
601  generator.MoveTouch(location);
602
603  // Try to drag |notmoved| window. This should not move the window.
604  {
605    gfx::Rect bounds = notmoved->bounds();
606    aura::test::EventGenerator gen(Shell::GetPrimaryRootWindow(),
607                                   notmoved.get());
608    gfx::Point start = notmoved->bounds().origin() + gfx::Vector2d(10, 10);
609    gfx::Point end = start + gfx::Vector2d(100, 10);
610    gen.GestureScrollSequence(start, end,
611        base::TimeDelta::FromMilliseconds(10),
612        10);
613    EXPECT_EQ(bounds.ToString(), notmoved->bounds().ToString());
614  }
615}
616
617// Verifies pressing escape resets the bounds to the original bounds.
618// Disabled crbug.com/166219.
619#if defined(OS_MACOSX) || defined(OS_WIN)
620#define MAYBE_EscapeReverts DISABLED_EscapeReverts
621#else
622#define MAYBE_EscapeReverts EscapeReverts
623#endif
624TEST_F(ToplevelWindowEventHandlerTest, MAYBE_EscapeReverts) {
625  scoped_ptr<aura::Window> target(CreateWindow(HTBOTTOMRIGHT));
626  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
627                                       target.get());
628  generator.PressLeftButton();
629  generator.MoveMouseBy(10, 11);
630
631  // Execute any scheduled draws so that pending mouse events are processed.
632  RunAllPendingInMessageLoop();
633
634  EXPECT_EQ("0,0 110x111", target->bounds().ToString());
635  generator.PressKey(ui::VKEY_ESCAPE, 0);
636  generator.ReleaseKey(ui::VKEY_ESCAPE, 0);
637  EXPECT_EQ("0,0 100x100", target->bounds().ToString());
638}
639
640// Verifies window minimization/maximization completes drag.
641// Disabled crbug.com/166219.
642#if defined(OS_WIN)
643#define MAYBE_MinimizeMaximizeCompletes DISABLED_MinimizeMaximizeCompletes
644#else
645#define MAYBE_MinimizeMaximizeCompletes MinimizeMaximizeCompletes
646#endif
647TEST_F(ToplevelWindowEventHandlerTest, MAYBE_MinimizeMaximizeCompletes) {
648  // Once window is minimized, window dragging completes.
649  {
650    scoped_ptr<aura::Window> target(CreateWindow(HTCAPTION));
651    target->Focus();
652    aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
653                                         target.get());
654    generator.PressLeftButton();
655    generator.MoveMouseBy(10, 11);
656    RunAllPendingInMessageLoop();
657    EXPECT_EQ("10,11 100x100", target->bounds().ToString());
658
659    wm::MinimizeWindow(target.get());
660    wm::RestoreWindow(target.get());
661
662    generator.PressLeftButton();
663    generator.MoveMouseBy(10, 11);
664    RunAllPendingInMessageLoop();
665    EXPECT_EQ("10,11 100x100", target->bounds().ToString());
666  }
667
668  // Once window is maximized, window dragging completes.
669  {
670    scoped_ptr<aura::Window> target(CreateWindow(HTCAPTION));
671    target->Focus();
672    aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
673                                         target.get());
674    generator.PressLeftButton();
675    generator.MoveMouseBy(10, 11);
676    RunAllPendingInMessageLoop();
677    EXPECT_EQ("10,11 100x100", target->bounds().ToString());
678
679    wm::MaximizeWindow(target.get());
680    wm::RestoreWindow(target.get());
681
682    generator.PressLeftButton();
683    generator.MoveMouseBy(10, 11);
684    RunAllPendingInMessageLoop();
685    EXPECT_EQ("10,11 100x100", target->bounds().ToString());
686  }
687}
688
689// Test class for mouse and touch resize shadow tests.
690class ToplevelWindowEventHandlerResizeTest
691    : public ToplevelWindowEventHandlerTest {
692 public:
693  ToplevelWindowEventHandlerResizeTest() : delegate_(NULL) {}
694  virtual ~ToplevelWindowEventHandlerResizeTest() {}
695
696  virtual void SetUp() OVERRIDE {
697    ToplevelWindowEventHandlerTest::SetUp();
698
699    delegate_ = new TestWindowDelegate(HTNOWHERE);
700    target_.reset(CreateTestWindowInShellWithDelegate(
701        delegate_, 0, gfx::Rect(0, 0, 100, 100)));
702
703    gfx::Insets mouse_insets = gfx::Insets(-ash::kResizeOutsideBoundsSize,
704                                           -ash::kResizeOutsideBoundsSize,
705                                           -ash::kResizeOutsideBoundsSize,
706                                           -ash::kResizeOutsideBoundsSize);
707    gfx::Insets touch_insets =
708        mouse_insets.Scale(ash::kResizeOutsideBoundsScaleForTouch);
709    target_->SetHitTestBoundsOverrideOuter(mouse_insets, touch_insets);
710    target_->set_hit_test_bounds_override_inner(mouse_insets);
711  }
712
713  virtual void TearDown() OVERRIDE {
714    target_.reset();
715    ToplevelWindowEventHandlerTest::TearDown();
716  }
717
718  // Called on each scroll event. Checks if the correct resize shadow is shown.
719  void ProcessEvent(ui::EventType type, const gfx::Vector2dF& delta) {
720    if (type == ui::ET_GESTURE_SCROLL_END) {
721      // After gesture scroll ends, there should be no resize shadow.
722      EXPECT_FALSE(HasResizeShadow());
723    } else {
724      // Check if there is a resize shadow under the correct border.
725      ASSERT_TRUE(HasResizeShadow());
726      EXPECT_EQ(HTBOTTOMRIGHT, ResizeShadowLastHitTest());
727    }
728  }
729
730 protected:
731  void SetHittestCode(int hittest_code) {
732    delegate_->set_window_component(hittest_code);
733  }
734
735  aura::Window* target() { return target_.get(); }
736
737  bool HasResizeShadow() const {
738    // There is no shadow if no ResizeShadow object is associated with the
739    // window or there is one but its hit test is set to HTNOWHERE. Since we
740    // don't want to tie tests to that implementation detail, both cases are
741    // considered here.
742    return ResizeShadow() && ResizeShadowLastHitTest() != HTNOWHERE;
743  }
744
745  int ResizeShadowLastHitTest() const {
746    return ResizeShadow()->GetLastHitTestForTest();
747  }
748
749 private:
750  internal::ResizeShadow* ResizeShadow() const {
751    return Shell::GetInstance()->resize_shadow_controller()->
752        GetShadowForWindowForTest(target_.get());
753  }
754
755  TestWindowDelegate* delegate_;
756  scoped_ptr<aura::Window> target_;
757
758  DISALLOW_COPY_AND_ASSIGN(ToplevelWindowEventHandlerResizeTest);
759};
760
761// Tests resize shadows for touch resizing.
762TEST_F(ToplevelWindowEventHandlerResizeTest, TouchResizeShadows) {
763  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), target());
764
765  // Drag bottom right border of the window and check for the resize shadows.
766  // Shadows are checked in the callback function.
767  SetHittestCode(HTBOTTOMRIGHT);
768  generator.GestureScrollSequenceWithCallback(
769      gfx::Point(105, 105),
770      gfx::Point(150, 150),
771      base::TimeDelta::FromMilliseconds(100),
772      3,
773      base::Bind(&ToplevelWindowEventHandlerResizeTest::ProcessEvent,
774                 base::Unretained(this)));
775  RunAllPendingInMessageLoop();
776}
777
778// Tests resize shadows for mouse resizing.
779TEST_F(ToplevelWindowEventHandlerResizeTest, MouseResizeShadows) {
780  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), target());
781
782  // There should be no shadow at the beginning.
783  EXPECT_FALSE(HasResizeShadow());
784
785  // Move mouse over the right border. Shadows should appear.
786  SetHittestCode(HTRIGHT);
787  generator.MoveMouseTo(gfx::Point(100, 50));
788  ASSERT_TRUE(HasResizeShadow());
789  EXPECT_EQ(HTRIGHT, ResizeShadowLastHitTest());
790
791  // Move mouse over the bottom right border. Shadows should stay.
792  SetHittestCode(HTBOTTOMRIGHT);
793  generator.MoveMouseTo(100, 100);
794  ASSERT_TRUE(HasResizeShadow());
795  EXPECT_EQ(HTBOTTOMRIGHT, ResizeShadowLastHitTest());
796
797  // Move mouse into the window. Shadows should disappear.
798  SetHittestCode(HTCLIENT);
799  generator.MoveMouseTo(50, 50);
800  EXPECT_FALSE(HasResizeShadow());
801
802  // Move mouse over the bottom order. Shadows should reappear.
803  SetHittestCode(HTBOTTOM);
804  generator.MoveMouseTo(50, 100);
805  ASSERT_TRUE(HasResizeShadow());
806  EXPECT_EQ(HTBOTTOM, ResizeShadowLastHitTest());
807
808  // Move mouse out of the window. Shadows should disappear.
809  generator.MoveMouseTo(150, 150);
810  EXPECT_FALSE(HasResizeShadow());
811
812  RunAllPendingInMessageLoop();
813}
814
815}  // namespace test
816}  // namespace ash
817