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/root_window_controller.h" 8 #include "ash/shell.h" 9 #include "ash/shell_window_ids.h" 10 #include "ash/test/ash_test_base.h" 11 #include "ash/wm/lock_state_controller.h" 12 #include "ash/wm/resize_shadow.h" 13 #include "ash/wm/resize_shadow_controller.h" 14 #include "ash/wm/window_state.h" 15 #include "ash/wm/window_util.h" 16 #include "ash/wm/workspace_controller.h" 17 #include "base/basictypes.h" 18 #include "base/compiler_specific.h" 19 #include "base/message_loop/message_loop.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 #include "ui/aura/client/aura_constants.h" 22 #include "ui/aura/test/aura_test_base.h" 23 #include "ui/aura/test/test_window_delegate.h" 24 #include "ui/aura/window_event_dispatcher.h" 25 #include "ui/base/hit_test.h" 26 #include "ui/events/event.h" 27 #include "ui/events/test/event_generator.h" 28 #include "ui/gfx/screen.h" 29 #include "ui/wm/core/window_util.h" 30 #include "ui/wm/public/window_move_client.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(aura::Window* window) 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(ui::wm::WINDOW_TYPE_NORMAL); 72 w1->set_id(1); 73 w1->Init(aura::WINDOW_LAYER_TEXTURED); 74 aura::Window* parent = Shell::GetContainer( 75 Shell::GetPrimaryRootWindow(), kShellWindowId_AlwaysOnTopContainer); 76 parent->AddChild(w1); 77 w1->SetBounds(gfx::Rect(0, 0, 100, 100)); 78 w1->Show(); 79 return w1; 80 } 81 82 void DragFromCenterBy(aura::Window* window, int dx, int dy) { 83 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), window); 84 generator.DragMouseBy(dx, dy); 85 } 86 87 void TouchDragFromCenterBy(aura::Window* window, int dx, int dy) { 88 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), window); 89 generator.PressMoveAndReleaseTouchBy(dx, dy); 90 } 91 92 scoped_ptr<ToplevelWindowEventHandler> handler_; 93 94 private: 95 DISALLOW_COPY_AND_ASSIGN(ToplevelWindowEventHandlerTest); 96 }; 97 98 } 99 100 TEST_F(ToplevelWindowEventHandlerTest, Caption) { 101 scoped_ptr<aura::Window> w1(CreateWindow(HTCAPTION)); 102 gfx::Size size = w1->bounds().size(); 103 DragFromCenterBy(w1.get(), 100, 100); 104 // Position should have been offset by 100,100. 105 EXPECT_EQ("100,100", w1->bounds().origin().ToString()); 106 // Size should not have. 107 EXPECT_EQ(size.ToString(), w1->bounds().size().ToString()); 108 109 TouchDragFromCenterBy(w1.get(), 100, 100); 110 // Position should have been offset by 100,100. 111 EXPECT_EQ("200,200", w1->bounds().origin().ToString()); 112 // Size should not have. 113 EXPECT_EQ(size.ToString(), w1->bounds().size().ToString()); 114 } 115 116 TEST_F(ToplevelWindowEventHandlerTest, BottomRight) { 117 scoped_ptr<aura::Window> w1(CreateWindow(HTBOTTOMRIGHT)); 118 gfx::Point position = w1->bounds().origin(); 119 DragFromCenterBy(w1.get(), 100, 100); 120 // Position should not have changed. 121 EXPECT_EQ(position.ToString(), w1->bounds().origin().ToString()); 122 // Size should have increased by 100,100. 123 EXPECT_EQ(gfx::Size(200, 200).ToString(), w1->bounds().size().ToString()); 124 } 125 126 TEST_F(ToplevelWindowEventHandlerTest, GrowBox) { 127 scoped_ptr<aura::Window> w1(CreateWindow(HTGROWBOX)); 128 TestWindowDelegate* window_delegate = 129 static_cast<TestWindowDelegate*>(w1->delegate()); 130 window_delegate->set_minimum_size(gfx::Size(40, 40)); 131 132 gfx::Point position = w1->bounds().origin(); 133 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 134 generator.MoveMouseToCenterOf(w1.get()); 135 generator.DragMouseBy(100, 100); 136 // Position should not have changed. 137 EXPECT_EQ(position.ToString(), w1->bounds().origin().ToString()); 138 // Size should have increased by 100,100. 139 EXPECT_EQ(gfx::Size(200, 200).ToString(), w1->bounds().size().ToString()); 140 141 // Shrink the wnidow by (-100, -100). 142 generator.DragMouseBy(-100, -100); 143 // Position should not have changed. 144 EXPECT_EQ(position.ToString(), w1->bounds().origin().ToString()); 145 // Size should have decreased by 100,100. 146 EXPECT_EQ(gfx::Size(100, 100).ToString(), w1->bounds().size().ToString()); 147 148 // Enforce minimum size. 149 generator.DragMouseBy(-60, -60); 150 EXPECT_EQ(position.ToString(), w1->bounds().origin().ToString()); 151 EXPECT_EQ(gfx::Size(40, 40).ToString(), w1->bounds().size().ToString()); 152 } 153 154 TEST_F(ToplevelWindowEventHandlerTest, Right) { 155 scoped_ptr<aura::Window> w1(CreateWindow(HTRIGHT)); 156 gfx::Point position = w1->bounds().origin(); 157 DragFromCenterBy(w1.get(), 100, 100); 158 // Position should not have changed. 159 EXPECT_EQ(position.ToString(), w1->bounds().origin().ToString()); 160 // Size should have increased by 100,0. 161 EXPECT_EQ(gfx::Size(200, 100).ToString(), w1->bounds().size().ToString()); 162 } 163 164 TEST_F(ToplevelWindowEventHandlerTest, Bottom) { 165 scoped_ptr<aura::Window> w1(CreateWindow(HTBOTTOM)); 166 gfx::Point position = w1->bounds().origin(); 167 DragFromCenterBy(w1.get(), 100, 100); 168 // Position should not have changed. 169 EXPECT_EQ(position.ToString(), w1->bounds().origin().ToString()); 170 // Size should have increased by 0,100. 171 EXPECT_EQ(gfx::Size(100, 200).ToString(), w1->bounds().size().ToString()); 172 } 173 174 TEST_F(ToplevelWindowEventHandlerTest, TopRight) { 175 scoped_ptr<aura::Window> w1(CreateWindow(HTTOPRIGHT)); 176 DragFromCenterBy(w1.get(), -50, 50); 177 // Position should have been offset by 0,50. 178 EXPECT_EQ(gfx::Point(0, 50).ToString(), w1->bounds().origin().ToString()); 179 // Size should have decreased by 50,50. 180 EXPECT_EQ(gfx::Size(50, 50).ToString(), w1->bounds().size().ToString()); 181 } 182 183 TEST_F(ToplevelWindowEventHandlerTest, Top) { 184 scoped_ptr<aura::Window> w1(CreateWindow(HTTOP)); 185 DragFromCenterBy(w1.get(), 50, 50); 186 // Position should have been offset by 0,50. 187 EXPECT_EQ(gfx::Point(0, 50).ToString(), w1->bounds().origin().ToString()); 188 // Size should have decreased by 0,50. 189 EXPECT_EQ(gfx::Size(100, 50).ToString(), w1->bounds().size().ToString()); 190 } 191 192 TEST_F(ToplevelWindowEventHandlerTest, Left) { 193 scoped_ptr<aura::Window> w1(CreateWindow(HTLEFT)); 194 DragFromCenterBy(w1.get(), 50, 50); 195 // Position should have been offset by 50,0. 196 EXPECT_EQ(gfx::Point(50, 0).ToString(), w1->bounds().origin().ToString()); 197 // Size should have decreased by 50,0. 198 EXPECT_EQ(gfx::Size(50, 100).ToString(), w1->bounds().size().ToString()); 199 } 200 201 TEST_F(ToplevelWindowEventHandlerTest, BottomLeft) { 202 scoped_ptr<aura::Window> w1(CreateWindow(HTBOTTOMLEFT)); 203 DragFromCenterBy(w1.get(), 50, -50); 204 // Position should have been offset by 50,0. 205 EXPECT_EQ(gfx::Point(50, 0).ToString(), w1->bounds().origin().ToString()); 206 // Size should have decreased by 50,50. 207 EXPECT_EQ(gfx::Size(50, 50).ToString(), w1->bounds().size().ToString()); 208 } 209 210 TEST_F(ToplevelWindowEventHandlerTest, TopLeft) { 211 scoped_ptr<aura::Window> w1(CreateWindow(HTTOPLEFT)); 212 DragFromCenterBy(w1.get(), 50, 50); 213 // Position should have been offset by 50,50. 214 EXPECT_EQ(gfx::Point(50, 50).ToString(), w1->bounds().origin().ToString()); 215 // Size should have decreased by 50,50. 216 EXPECT_EQ(gfx::Size(50, 50).ToString(), w1->bounds().size().ToString()); 217 } 218 219 TEST_F(ToplevelWindowEventHandlerTest, Client) { 220 scoped_ptr<aura::Window> w1(CreateWindow(HTCLIENT)); 221 gfx::Rect bounds = w1->bounds(); 222 DragFromCenterBy(w1.get(), 100, 100); 223 // Neither position nor size should have changed. 224 EXPECT_EQ(bounds.ToString(), w1->bounds().ToString()); 225 } 226 227 TEST_F(ToplevelWindowEventHandlerTest, LeftPastMinimum) { 228 scoped_ptr<aura::Window> w1(CreateWindow(HTLEFT)); 229 TestWindowDelegate* window_delegate = 230 static_cast<TestWindowDelegate*>(w1->delegate()); 231 window_delegate->set_minimum_size(gfx::Size(40, 40)); 232 233 // Simulate a large left-to-right drag. Window width should be clamped to 234 // minimum and position change should be limited as well. 235 DragFromCenterBy(w1.get(), 333, 0); 236 EXPECT_EQ(gfx::Point(60, 0).ToString(), w1->bounds().origin().ToString()); 237 EXPECT_EQ(gfx::Size(40, 100).ToString(), w1->bounds().size().ToString()); 238 } 239 240 TEST_F(ToplevelWindowEventHandlerTest, RightPastMinimum) { 241 scoped_ptr<aura::Window> w1(CreateWindow(HTRIGHT)); 242 TestWindowDelegate* window_delegate = 243 static_cast<TestWindowDelegate*>(w1->delegate()); 244 window_delegate->set_minimum_size(gfx::Size(40, 40)); 245 gfx::Point position = w1->bounds().origin(); 246 247 // Simulate a large right-to-left drag. Window width should be clamped to 248 // minimum and position should not change. 249 DragFromCenterBy(w1.get(), -333, 0); 250 EXPECT_EQ(position.ToString(), w1->bounds().origin().ToString()); 251 EXPECT_EQ(gfx::Size(40, 100).ToString(), w1->bounds().size().ToString()); 252 } 253 254 TEST_F(ToplevelWindowEventHandlerTest, TopLeftPastMinimum) { 255 scoped_ptr<aura::Window> w1(CreateWindow(HTTOPLEFT)); 256 TestWindowDelegate* window_delegate = 257 static_cast<TestWindowDelegate*>(w1->delegate()); 258 window_delegate->set_minimum_size(gfx::Size(40, 40)); 259 260 // Simulate a large top-left to bottom-right drag. Window width should be 261 // clamped to minimum and position should be limited. 262 DragFromCenterBy(w1.get(), 333, 444); 263 EXPECT_EQ(gfx::Point(60, 60).ToString(), w1->bounds().origin().ToString()); 264 EXPECT_EQ(gfx::Size(40, 40).ToString(), w1->bounds().size().ToString()); 265 } 266 267 TEST_F(ToplevelWindowEventHandlerTest, TopRightPastMinimum) { 268 scoped_ptr<aura::Window> w1(CreateWindow(HTTOPRIGHT)); 269 TestWindowDelegate* window_delegate = 270 static_cast<TestWindowDelegate*>(w1->delegate()); 271 window_delegate->set_minimum_size(gfx::Size(40, 40)); 272 273 // Simulate a large top-right to bottom-left drag. Window size should be 274 // clamped to minimum, x position should not change, and y position should 275 // be clamped. 276 DragFromCenterBy(w1.get(), -333, 444); 277 EXPECT_EQ(gfx::Point(0, 60).ToString(), w1->bounds().origin().ToString()); 278 EXPECT_EQ(gfx::Size(40, 40).ToString(), w1->bounds().size().ToString()); 279 } 280 281 TEST_F(ToplevelWindowEventHandlerTest, BottomLeftPastMinimum) { 282 scoped_ptr<aura::Window> w1(CreateWindow(HTBOTTOMLEFT)); 283 TestWindowDelegate* window_delegate = 284 static_cast<TestWindowDelegate*>(w1->delegate()); 285 window_delegate->set_minimum_size(gfx::Size(40, 40)); 286 287 // Simulate a large bottom-left to top-right drag. Window size should be 288 // clamped to minimum, x position should be clamped, and y position should 289 // not change. 290 DragFromCenterBy(w1.get(), 333, -444); 291 EXPECT_EQ(gfx::Point(60, 0).ToString(), w1->bounds().origin().ToString()); 292 EXPECT_EQ(gfx::Size(40, 40).ToString(), w1->bounds().size().ToString()); 293 } 294 295 TEST_F(ToplevelWindowEventHandlerTest, BottomRightPastMinimum) { 296 scoped_ptr<aura::Window> w1(CreateWindow(HTBOTTOMRIGHT)); 297 TestWindowDelegate* window_delegate = 298 static_cast<TestWindowDelegate*>(w1->delegate()); 299 window_delegate->set_minimum_size(gfx::Size(40, 40)); 300 gfx::Point position = w1->bounds().origin(); 301 302 // Simulate a large bottom-right to top-left drag. Window size should be 303 // clamped to minimum and position should not change. 304 DragFromCenterBy(w1.get(), -333, -444); 305 EXPECT_EQ(position.ToString(), w1->bounds().origin().ToString()); 306 EXPECT_EQ(gfx::Size(40, 40).ToString(), w1->bounds().size().ToString()); 307 } 308 309 TEST_F(ToplevelWindowEventHandlerTest, BottomRightWorkArea) { 310 scoped_ptr<aura::Window> target(CreateWindow(HTBOTTOMRIGHT)); 311 gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow( 312 target.get()).work_area(); 313 gfx::Point position = target->bounds().origin(); 314 // Drag further than work_area bottom. 315 DragFromCenterBy(target.get(), 100, work_area.height()); 316 // Position should not have changed. 317 EXPECT_EQ(position.ToString(), target->bounds().origin().ToString()); 318 // Size should have increased by 100, work_area.height() - target->bounds.y() 319 EXPECT_EQ( 320 gfx::Size(200, work_area.height() - target->bounds().y()).ToString(), 321 target->bounds().size().ToString()); 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( 336 gfx::Size(130, work_area.height() - target->bounds().y()).ToString(), 337 target->bounds().size().ToString()); 338 } 339 340 TEST_F(ToplevelWindowEventHandlerTest, BottomWorkArea) { 341 scoped_ptr<aura::Window> target(CreateWindow(HTBOTTOM)); 342 gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow( 343 target.get()).work_area(); 344 gfx::Point position = target->bounds().origin(); 345 // Drag further than work_area bottom. 346 DragFromCenterBy(target.get(), 0, work_area.height()); 347 // Position should not have changed. 348 EXPECT_EQ(position.ToString(), target->bounds().origin().ToString()); 349 // Size should have increased by 0, work_area.height() - target->bounds.y() 350 EXPECT_EQ( 351 gfx::Size(100, work_area.height() - target->bounds().y()).ToString(), 352 target->bounds().size().ToString()); 353 } 354 355 TEST_F(ToplevelWindowEventHandlerTest, DontDragIfModalChild) { 356 scoped_ptr<aura::Window> w1(CreateWindow(HTCAPTION)); 357 scoped_ptr<aura::Window> w2(CreateWindow(HTCAPTION)); 358 w2->SetBounds(gfx::Rect(100, 0, 100, 100)); 359 w2->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); 360 ::wm::AddTransientChild(w1.get(), w2.get()); 361 gfx::Size size = w1->bounds().size(); 362 363 // Attempt to drag w1, position and size should not change because w1 has a 364 // modal child. 365 DragFromCenterBy(w1.get(), 100, 100); 366 EXPECT_EQ("0,0", w1->bounds().origin().ToString()); 367 EXPECT_EQ(size.ToString(), w1->bounds().size().ToString()); 368 369 TouchDragFromCenterBy(w1.get(), 100, 100); 370 EXPECT_EQ("0,0", w1->bounds().origin().ToString()); 371 EXPECT_EQ(size.ToString(), w1->bounds().size().ToString()); 372 } 373 374 // Verifies we don't let windows drag to a -y location. 375 TEST_F(ToplevelWindowEventHandlerTest, DontDragToNegativeY) { 376 scoped_ptr<aura::Window> target(CreateWindow(HTTOP)); 377 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 378 target.get()); 379 generator.MoveMouseTo(0, 5); 380 generator.DragMouseBy(0, -5); 381 // The y location and height should not have changed. 382 EXPECT_EQ(0, target->bounds().y()); 383 EXPECT_EQ(100, target->bounds().height()); 384 } 385 386 // Verifies we don't let windows go bigger than the display width. 387 TEST_F(ToplevelWindowEventHandlerTest, DontGotWiderThanScreen) { 388 scoped_ptr<aura::Window> target(CreateWindow(HTRIGHT)); 389 gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow( 390 target.get()).bounds(); 391 DragFromCenterBy(target.get(), work_area.width() * 2, 0); 392 // The y location and height should not have changed. 393 EXPECT_EQ(work_area.width(), target->bounds().width()); 394 } 395 396 // Verifies that touch-gestures drag the window correctly. 397 TEST_F(ToplevelWindowEventHandlerTest, GestureDrag) { 398 scoped_ptr<aura::Window> target( 399 CreateTestWindowInShellWithDelegate( 400 new TestWindowDelegate(HTCAPTION), 401 0, 402 gfx::Rect(0, 0, 100, 100))); 403 wm::WindowState* window_state = wm::GetWindowState(target.get()); 404 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 405 target.get()); 406 gfx::Rect old_bounds = target->bounds(); 407 gfx::Point location(5, 5); 408 target->SetProperty(aura::client::kCanMaximizeKey, true); 409 410 gfx::Point end = location; 411 412 // Snap right; 413 end.Offset(100, 0); 414 generator.GestureScrollSequence(location, end, 415 base::TimeDelta::FromMilliseconds(5), 416 10); 417 RunAllPendingInMessageLoop(); 418 419 // Verify that the window has moved after the gesture. 420 EXPECT_NE(old_bounds.ToString(), target->bounds().ToString()); 421 EXPECT_EQ(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED, window_state->GetStateType()); 422 423 old_bounds = target->bounds(); 424 425 // Snap left. 426 end = location = target->GetBoundsInRootWindow().CenterPoint(); 427 end.Offset(-100, 0); 428 generator.GestureScrollSequence(location, end, 429 base::TimeDelta::FromMilliseconds(5), 430 10); 431 RunAllPendingInMessageLoop(); 432 433 EXPECT_NE(old_bounds.ToString(), target->bounds().ToString()); 434 EXPECT_EQ(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType()); 435 436 gfx::Rect bounds_before_maximization = target->bounds(); 437 bounds_before_maximization.Offset(0, 100); 438 target->SetBounds(bounds_before_maximization); 439 old_bounds = target->bounds(); 440 441 // Maximize. 442 end = location = target->GetBoundsInRootWindow().CenterPoint(); 443 end.Offset(0, -100); 444 generator.GestureScrollSequence(location, end, 445 base::TimeDelta::FromMilliseconds(5), 446 10); 447 RunAllPendingInMessageLoop(); 448 449 EXPECT_NE(old_bounds.ToString(), target->bounds().ToString()); 450 EXPECT_TRUE(window_state->IsMaximized()); 451 EXPECT_EQ(old_bounds.ToString(), 452 window_state->GetRestoreBoundsInScreen().ToString()); 453 454 window_state->Restore(); 455 target->SetBounds(old_bounds); 456 457 // Minimize. 458 end = location = target->GetBoundsInRootWindow().CenterPoint(); 459 end.Offset(0, 100); 460 generator.GestureScrollSequence(location, end, 461 base::TimeDelta::FromMilliseconds(5), 462 10); 463 RunAllPendingInMessageLoop(); 464 EXPECT_NE(old_bounds.ToString(), target->bounds().ToString()); 465 EXPECT_TRUE(window_state->IsMinimized()); 466 EXPECT_TRUE(window_state->unminimize_to_restore_bounds()); 467 EXPECT_EQ(old_bounds.ToString(), 468 window_state->GetRestoreBoundsInScreen().ToString()); 469 } 470 471 // Tests that a gesture cannot minimize a window in login/lock screen. 472 TEST_F(ToplevelWindowEventHandlerTest, GestureDragMinimizeLoginScreen) { 473 LockStateController* state_controller = 474 Shell::GetInstance()->lock_state_controller(); 475 state_controller->OnLoginStateChanged(user::LOGGED_IN_NONE); 476 state_controller->OnLockStateChanged(false); 477 SetUserLoggedIn(false); 478 479 scoped_ptr<aura::Window> target(CreateWindow(HTCAPTION)); 480 aura::Window* lock = 481 RootWindowController::ForWindow(target.get()) 482 ->GetContainer(kShellWindowId_LockSystemModalContainer); 483 lock->AddChild(target.get()); 484 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 485 target.get()); 486 gfx::Rect old_bounds = target->bounds(); 487 gfx::Point location(5, 5); 488 target->SetProperty(aura::client::kCanMaximizeKey, true); 489 490 gfx::Point end = location; 491 end.Offset(0, 100); 492 generator.GestureScrollSequence(location, end, 493 base::TimeDelta::FromMilliseconds(5), 494 10); 495 RunAllPendingInMessageLoop(); 496 EXPECT_FALSE(wm::GetWindowState(target.get())->IsMinimized()); 497 } 498 499 TEST_F(ToplevelWindowEventHandlerTest, GestureDragToRestore) { 500 scoped_ptr<aura::Window> window( 501 CreateTestWindowInShellWithDelegate( 502 new TestWindowDelegate(HTCAPTION), 503 0, 504 gfx::Rect(10, 20, 30, 40))); 505 window->Show(); 506 wm::WindowState* window_state = wm::GetWindowState(window.get()); 507 window_state->Activate(); 508 509 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 510 window.get()); 511 gfx::Rect old_bounds = window->bounds(); 512 gfx::Point location, end; 513 end = location = window->GetBoundsInRootWindow().CenterPoint(); 514 end.Offset(0, 100); 515 generator.GestureScrollSequence(location, end, 516 base::TimeDelta::FromMilliseconds(5), 517 10); 518 RunAllPendingInMessageLoop(); 519 EXPECT_NE(old_bounds.ToString(), window->bounds().ToString()); 520 EXPECT_TRUE(window_state->IsMinimized()); 521 EXPECT_TRUE(window_state->unminimize_to_restore_bounds()); 522 EXPECT_EQ(old_bounds.ToString(), 523 window_state->GetRestoreBoundsInScreen().ToString()); 524 } 525 526 // Tests that an unresizable window cannot be dragged or snapped using gestures. 527 TEST_F(ToplevelWindowEventHandlerTest, GestureDragForUnresizableWindow) { 528 scoped_ptr<aura::Window> target(CreateWindow(HTCAPTION)); 529 wm::WindowState* window_state = wm::GetWindowState(target.get()); 530 531 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 532 target.get()); 533 gfx::Rect old_bounds = target->bounds(); 534 gfx::Point location(5, 5); 535 536 target->SetProperty(aura::client::kCanResizeKey, false); 537 538 gfx::Point end = location; 539 540 // Try to snap right. The window is not resizable. So it should not snap. 541 end.Offset(100, 0); 542 generator.GestureScrollSequence(location, end, 543 base::TimeDelta::FromMilliseconds(5), 544 10); 545 RunAllPendingInMessageLoop(); 546 547 // Verify that the window has moved after the gesture. 548 gfx::Rect expected_bounds(old_bounds); 549 expected_bounds.Offset(gfx::Vector2d(100, 0)); 550 EXPECT_EQ(expected_bounds.ToString(), target->bounds().ToString()); 551 552 // Verify that the window did not snap left. 553 EXPECT_TRUE(window_state->IsNormalStateType()); 554 555 old_bounds = target->bounds(); 556 557 // Try to snap left. It should not snap. 558 end = location = target->GetBoundsInRootWindow().CenterPoint(); 559 end.Offset(-100, 0); 560 generator.GestureScrollSequence(location, end, 561 base::TimeDelta::FromMilliseconds(5), 562 10); 563 RunAllPendingInMessageLoop(); 564 565 // Verify that the window has moved after the gesture. 566 expected_bounds = old_bounds; 567 expected_bounds.Offset(gfx::Vector2d(-100, 0)); 568 EXPECT_EQ(expected_bounds.ToString(), target->bounds().ToString()); 569 570 // Verify that the window did not snap left. 571 EXPECT_TRUE(window_state->IsNormalStateType()); 572 } 573 574 // Tests that dragging multiple windows at the same time is not allowed. 575 TEST_F(ToplevelWindowEventHandlerTest, GestureDragMultipleWindows) { 576 scoped_ptr<aura::Window> target( 577 CreateTestWindowInShellWithDelegate( 578 new TestWindowDelegate(HTCAPTION), 579 0, 580 gfx::Rect(0, 0, 100, 100))); 581 scoped_ptr<aura::Window> notmoved( 582 CreateTestWindowInShellWithDelegate( 583 new TestWindowDelegate(HTCAPTION), 584 1, gfx::Rect(100, 0, 100, 100))); 585 586 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 587 target.get()); 588 gfx::Rect old_bounds = target->bounds(); 589 gfx::Point location(5, 5); 590 target->SetProperty(aura::client::kCanMaximizeKey, true); 591 592 // Send some touch events to start dragging |target|. 593 generator.MoveTouch(location); 594 generator.PressTouch(); 595 location.Offset(40, 5); 596 generator.MoveTouch(location); 597 598 // Try to drag |notmoved| window. This should not move the window. 599 { 600 gfx::Rect bounds = notmoved->bounds(); 601 ui::test::EventGenerator gen(Shell::GetPrimaryRootWindow(), notmoved.get()); 602 gfx::Point start = notmoved->bounds().origin() + gfx::Vector2d(10, 10); 603 gfx::Point end = start + gfx::Vector2d(100, 10); 604 gen.GestureScrollSequence(start, end, 605 base::TimeDelta::FromMilliseconds(10), 606 10); 607 EXPECT_EQ(bounds.ToString(), notmoved->bounds().ToString()); 608 } 609 } 610 611 // Verifies pressing escape resets the bounds to the original bounds. 612 // Disabled crbug.com/166219. 613 #if defined(OS_WIN) 614 #define MAYBE_EscapeReverts DISABLED_EscapeReverts 615 #else 616 #define MAYBE_EscapeReverts EscapeReverts 617 #endif 618 TEST_F(ToplevelWindowEventHandlerTest, MAYBE_EscapeReverts) { 619 scoped_ptr<aura::Window> target(CreateWindow(HTBOTTOMRIGHT)); 620 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 621 target.get()); 622 generator.PressLeftButton(); 623 generator.MoveMouseBy(10, 11); 624 625 // Execute any scheduled draws so that pending mouse events are processed. 626 RunAllPendingInMessageLoop(); 627 628 EXPECT_EQ("0,0 110x111", target->bounds().ToString()); 629 generator.PressKey(ui::VKEY_ESCAPE, 0); 630 generator.ReleaseKey(ui::VKEY_ESCAPE, 0); 631 EXPECT_EQ("0,0 100x100", target->bounds().ToString()); 632 } 633 634 // Verifies window minimization/maximization completes drag. 635 // Disabled crbug.com/166219. 636 #if defined(OS_WIN) 637 #define MAYBE_MinimizeMaximizeCompletes DISABLED_MinimizeMaximizeCompletes 638 #else 639 #define MAYBE_MinimizeMaximizeCompletes MinimizeMaximizeCompletes 640 #endif 641 TEST_F(ToplevelWindowEventHandlerTest, MAYBE_MinimizeMaximizeCompletes) { 642 // Once window is minimized, window dragging completes. 643 { 644 scoped_ptr<aura::Window> target(CreateWindow(HTCAPTION)); 645 target->Focus(); 646 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 647 target.get()); 648 generator.PressLeftButton(); 649 generator.MoveMouseBy(10, 11); 650 RunAllPendingInMessageLoop(); 651 EXPECT_EQ("10,11 100x100", target->bounds().ToString()); 652 wm::WindowState* window_state = wm::GetWindowState(target.get()); 653 window_state->Minimize(); 654 window_state->Restore(); 655 656 generator.PressLeftButton(); 657 generator.MoveMouseBy(10, 11); 658 RunAllPendingInMessageLoop(); 659 EXPECT_EQ("10,11 100x100", target->bounds().ToString()); 660 } 661 662 // Once window is maximized, window dragging completes. 663 { 664 scoped_ptr<aura::Window> target(CreateWindow(HTCAPTION)); 665 target->Focus(); 666 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 667 target.get()); 668 generator.PressLeftButton(); 669 generator.MoveMouseBy(10, 11); 670 RunAllPendingInMessageLoop(); 671 EXPECT_EQ("10,11 100x100", target->bounds().ToString()); 672 wm::WindowState* window_state = wm::GetWindowState(target.get()); 673 window_state->Maximize(); 674 window_state->Restore(); 675 676 generator.PressLeftButton(); 677 generator.MoveMouseBy(10, 11); 678 RunAllPendingInMessageLoop(); 679 EXPECT_EQ("10,11 100x100", target->bounds().ToString()); 680 } 681 } 682 683 // Verifies that a drag cannot be started via 684 // aura::client::WindowMoveClient::RunMoveLoop() while another drag is already 685 // in progress. 686 TEST_F(ToplevelWindowEventHandlerTest, RunMoveLoopFailsDuringInProgressDrag) { 687 scoped_ptr<aura::Window> window1(CreateWindow(HTCAPTION)); 688 EXPECT_EQ("0,0 100x100", window1->bounds().ToString()); 689 scoped_ptr<aura::Window> window2(CreateWindow(HTCAPTION)); 690 691 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 692 window1.get()); 693 window1->Focus(); 694 generator.PressLeftButton(); 695 generator.MoveMouseBy(10, 11); 696 EXPECT_EQ("10,11 100x100", window1->bounds().ToString()); 697 698 aura::client::WindowMoveClient* move_client = 699 aura::client::GetWindowMoveClient(window2->GetRootWindow()); 700 EXPECT_EQ(aura::client::MOVE_CANCELED, 701 move_client->RunMoveLoop(window2.get(), gfx::Vector2d(), 702 aura::client::WINDOW_MOVE_SOURCE_MOUSE)); 703 704 generator.ReleaseLeftButton(); 705 EXPECT_EQ("10,11 100x100", window1->bounds().ToString()); 706 } 707 708 namespace { 709 710 void SendMouseReleaseAndReleaseCapture(ui::test::EventGenerator* generator, 711 aura::Window* window) { 712 generator->ReleaseLeftButton(); 713 window->ReleaseCapture(); 714 } 715 716 } // namespace 717 718 // Test that a drag is successful even if ET_MOUSE_CAPTURE_CHANGED is sent 719 // immediately after the mouse release. views::Widget has this behavior. 720 TEST_F(ToplevelWindowEventHandlerTest, CaptureLossAfterMouseRelease) { 721 scoped_ptr<aura::Window> window(CreateWindow(HTNOWHERE)); 722 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 723 window.get()); 724 generator.PressLeftButton(); 725 window->SetCapture(); 726 727 aura::client::WindowMoveClient* move_client = 728 aura::client::GetWindowMoveClient(window->GetRootWindow()); 729 base::MessageLoopForUI::current()->PostTask( 730 FROM_HERE, 731 base::Bind(&SendMouseReleaseAndReleaseCapture, 732 base::Unretained(&generator), 733 base::Unretained(window.get()))); 734 EXPECT_EQ(aura::client::MOVE_SUCCESSFUL, 735 move_client->RunMoveLoop(window.get(), gfx::Vector2d(), 736 aura::client::WINDOW_MOVE_SOURCE_MOUSE)); 737 } 738 739 // Showing the resize shadows when the mouse is over the window edges is tested 740 // in resize_shadow_and_cursor_test.cc 741 742 } // namespace test 743 } // namespace ash 744