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