Home | History | Annotate | Download | only in wm
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ash/wm/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 
     39 namespace ash {
     40 namespace test {
     41 
     42 namespace {
     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.
     46 class 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 
     62 class 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 
    101 TEST_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 
    117 TEST_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 
    127 TEST_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 
    155 TEST_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 
    165 TEST_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 
    175 TEST_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 
    184 TEST_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 
    193 TEST_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 
    202 TEST_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 
    211 TEST_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 
    220 TEST_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 
    228 TEST_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 
    241 TEST_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 
    255 TEST_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 
    268 TEST_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 
    282 TEST_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 
    296 TEST_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 
    310 TEST_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 
    324 TEST_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 
    339 TEST_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.
    354 TEST_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.
    366 TEST_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.
    376 TEST_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.
    464 TEST_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 
    491 TEST_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.
    518 TEST_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.
    580 TEST_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
    624 TEST_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
    647 TEST_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.
    690 class 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.
    762 TEST_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.
    779 TEST_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