1// Copyright 2013 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/workspace_controller.h" 6 7#include <map> 8 9#include "ash/ash_switches.h" 10#include "ash/root_window_controller.h" 11#include "ash/screen_ash.h" 12#include "ash/shelf/shelf_layout_manager.h" 13#include "ash/shelf/shelf_widget.h" 14#include "ash/shell.h" 15#include "ash/shell_window_ids.h" 16#include "ash/system/status_area_widget.h" 17#include "ash/test/ash_test_base.h" 18#include "ash/test/shell_test_api.h" 19#include "ash/wm/window_state.h" 20#include "ash/wm/window_util.h" 21#include "base/command_line.h" 22#include "base/strings/string_number_conversions.h" 23#include "ui/aura/client/aura_constants.h" 24#include "ui/aura/root_window.h" 25#include "ui/aura/test/event_generator.h" 26#include "ui/aura/test/test_window_delegate.h" 27#include "ui/aura/test/test_windows.h" 28#include "ui/aura/window.h" 29#include "ui/base/hit_test.h" 30#include "ui/base/ui_base_types.h" 31#include "ui/compositor/layer.h" 32#include "ui/compositor/scoped_animation_duration_scale_mode.h" 33#include "ui/gfx/screen.h" 34#include "ui/views/corewm/window_animations.h" 35#include "ui/views/widget/widget.h" 36 37using aura::Window; 38 39namespace ash { 40namespace internal { 41 42// Returns a string containing the names of all the children of |window| (in 43// order). Each entry is separated by a space. 44std::string GetWindowNames(const aura::Window* window) { 45 std::string result; 46 for (size_t i = 0; i < window->children().size(); ++i) { 47 if (i != 0) 48 result += " "; 49 result += window->children()[i]->name(); 50 } 51 return result; 52} 53 54// Returns a string containing the names of windows corresponding to each of the 55// child layers of |window|'s layer. Any layers that don't correspond to a child 56// Window of |window| are ignored. The result is ordered based on the layer 57// ordering. 58std::string GetLayerNames(const aura::Window* window) { 59 typedef std::map<const ui::Layer*, std::string> LayerToWindowNameMap; 60 LayerToWindowNameMap window_names; 61 for (size_t i = 0; i < window->children().size(); ++i) { 62 window_names[window->children()[i]->layer()] = 63 window->children()[i]->name(); 64 } 65 66 std::string result; 67 const std::vector<ui::Layer*>& layers(window->layer()->children()); 68 for (size_t i = 0; i < layers.size(); ++i) { 69 LayerToWindowNameMap::iterator layer_i = 70 window_names.find(layers[i]); 71 if (layer_i != window_names.end()) { 72 if (!result.empty()) 73 result += " "; 74 result += layer_i->second; 75 } 76 } 77 return result; 78} 79 80class WorkspaceControllerTest : public test::AshTestBase { 81 public: 82 WorkspaceControllerTest() {} 83 virtual ~WorkspaceControllerTest() {} 84 85 aura::Window* CreateTestWindowUnparented() { 86 aura::Window* window = new aura::Window(NULL); 87 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 88 window->SetType(aura::client::WINDOW_TYPE_NORMAL); 89 window->Init(ui::LAYER_TEXTURED); 90 return window; 91 } 92 93 aura::Window* CreateTestWindow() { 94 aura::Window* window = new aura::Window(NULL); 95 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 96 window->SetType(aura::client::WINDOW_TYPE_NORMAL); 97 window->Init(ui::LAYER_TEXTURED); 98 ParentWindowInPrimaryRootWindow(window); 99 return window; 100 } 101 102 aura::Window* CreateBrowserLikeWindow(const gfx::Rect& bounds) { 103 aura::Window* window = CreateTestWindow(); 104 window->SetBounds(bounds); 105 wm::WindowState* window_state = wm::GetWindowState(window); 106 window_state->set_window_position_managed(true); 107 window->Show(); 108 return window; 109 } 110 111 aura::Window* CreatePopupLikeWindow(const gfx::Rect& bounds) { 112 aura::Window* window = CreateTestWindowInShellWithBounds(bounds); 113 window->Show(); 114 return window; 115 } 116 117 aura::Window* GetDesktop() { 118 return Shell::GetContainer(Shell::GetPrimaryRootWindow(), 119 kShellWindowId_DefaultContainer); 120 } 121 122 gfx::Rect GetFullscreenBounds(aura::Window* window) { 123 return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds(); 124 } 125 126 ShelfWidget* shelf_widget() { 127 return Shell::GetPrimaryRootWindowController()->shelf(); 128 } 129 130 ShelfLayoutManager* shelf_layout_manager() { 131 return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager(); 132 } 133 134 bool GetWindowOverlapsShelf() { 135 return shelf_layout_manager()->window_overlaps_shelf(); 136 } 137 138 private: 139 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest); 140}; 141 142// Assertions around adding a normal window. 143TEST_F(WorkspaceControllerTest, AddNormalWindowWhenEmpty) { 144 scoped_ptr<Window> w1(CreateTestWindow()); 145 w1->SetBounds(gfx::Rect(0, 0, 250, 251)); 146 147 wm::WindowState* window_state = wm::GetWindowState(w1.get()); 148 149 EXPECT_FALSE(window_state->HasRestoreBounds()); 150 151 w1->Show(); 152 153 EXPECT_FALSE(window_state->HasRestoreBounds()); 154 155 ASSERT_TRUE(w1->layer() != NULL); 156 EXPECT_TRUE(w1->layer()->visible()); 157 158 EXPECT_EQ("0,0 250x251", w1->bounds().ToString()); 159 160 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 161} 162 163// Assertions around maximizing/unmaximizing. 164TEST_F(WorkspaceControllerTest, SingleMaximizeWindow) { 165 scoped_ptr<Window> w1(CreateTestWindow()); 166 w1->SetBounds(gfx::Rect(0, 0, 250, 251)); 167 168 w1->Show(); 169 wm::ActivateWindow(w1.get()); 170 171 EXPECT_TRUE(wm::IsActiveWindow(w1.get())); 172 173 ASSERT_TRUE(w1->layer() != NULL); 174 EXPECT_TRUE(w1->layer()->visible()); 175 176 EXPECT_EQ("0,0 250x251", w1->bounds().ToString()); 177 178 // Maximize the window. 179 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 180 181 EXPECT_TRUE(wm::IsActiveWindow(w1.get())); 182 183 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 184 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()).width(), 185 w1->bounds().width()); 186 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()).height(), 187 w1->bounds().height()); 188 189 // Restore the window. 190 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 191 192 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 193 EXPECT_EQ("0,0 250x251", w1->bounds().ToString()); 194} 195 196// Assertions around two windows and toggling one to be fullscreen. 197TEST_F(WorkspaceControllerTest, FullscreenWithNormalWindow) { 198 scoped_ptr<Window> w1(CreateTestWindow()); 199 scoped_ptr<Window> w2(CreateTestWindow()); 200 w1->SetBounds(gfx::Rect(0, 0, 250, 251)); 201 w1->Show(); 202 203 ASSERT_TRUE(w1->layer() != NULL); 204 EXPECT_TRUE(w1->layer()->visible()); 205 206 w2->SetBounds(gfx::Rect(0, 0, 50, 51)); 207 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 208 w2->Show(); 209 wm::ActivateWindow(w2.get()); 210 211 // Both windows should be in the same workspace. 212 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 213 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]); 214 215 gfx::Rect work_area( 216 ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get())); 217 EXPECT_EQ(work_area.width(), w2->bounds().width()); 218 EXPECT_EQ(work_area.height(), w2->bounds().height()); 219 220 // Restore w2, which should then go back to one workspace. 221 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 222 EXPECT_EQ(50, w2->bounds().width()); 223 EXPECT_EQ(51, w2->bounds().height()); 224 EXPECT_TRUE(wm::IsActiveWindow(w2.get())); 225} 226 227// Makes sure requests to change the bounds of a normal window go through. 228TEST_F(WorkspaceControllerTest, ChangeBoundsOfNormalWindow) { 229 scoped_ptr<Window> w1(CreateTestWindow()); 230 w1->Show(); 231 232 // Setting the bounds should go through since the window is in the normal 233 // workspace. 234 w1->SetBounds(gfx::Rect(0, 0, 200, 500)); 235 EXPECT_EQ(200, w1->bounds().width()); 236 EXPECT_EQ(500, w1->bounds().height()); 237} 238 239// Verifies the bounds is not altered when showing and grid is enabled. 240TEST_F(WorkspaceControllerTest, SnapToGrid) { 241 scoped_ptr<Window> w1(CreateTestWindowUnparented()); 242 w1->SetBounds(gfx::Rect(1, 6, 25, 30)); 243 ParentWindowInPrimaryRootWindow(w1.get()); 244 // We are not aligning this anymore this way. When the window gets shown 245 // the window is expected to be handled differently, but this cannot be 246 // tested with this test. So the result of this test should be that the 247 // bounds are exactly as passed in. 248 EXPECT_EQ("1,6 25x30", w1->bounds().ToString()); 249} 250 251// Assertions around a fullscreen window. 252TEST_F(WorkspaceControllerTest, SingleFullscreenWindow) { 253 scoped_ptr<Window> w1(CreateTestWindow()); 254 w1->SetBounds(gfx::Rect(0, 0, 250, 251)); 255 // Make the window fullscreen. 256 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 257 w1->Show(); 258 wm::ActivateWindow(w1.get()); 259 260 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 261 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width()); 262 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height()); 263 264 // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up 265 // with when using views::Widget. 266 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT); 267 EXPECT_EQ("0,0 250x251", w1->bounds().ToString()); 268 269 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 270 EXPECT_EQ(250, w1->bounds().width()); 271 EXPECT_EQ(251, w1->bounds().height()); 272 273 // Back to fullscreen. 274 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 275 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 276 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width()); 277 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height()); 278 wm::WindowState* window_state = wm::GetWindowState(w1.get()); 279 280 ASSERT_TRUE(window_state->HasRestoreBounds()); 281 EXPECT_EQ("0,0 250x251", window_state->GetRestoreBoundsInScreen().ToString()); 282} 283 284// Assertions around minimizing a single window. 285TEST_F(WorkspaceControllerTest, MinimizeSingleWindow) { 286 scoped_ptr<Window> w1(CreateTestWindow()); 287 288 w1->Show(); 289 290 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 291 EXPECT_FALSE(w1->layer()->IsDrawn()); 292 293 // Show the window. 294 w1->Show(); 295 EXPECT_TRUE(wm::GetWindowState(w1.get())->IsNormalShowState()); 296 EXPECT_TRUE(w1->layer()->IsDrawn()); 297} 298 299// Assertions around minimizing a fullscreen window. 300TEST_F(WorkspaceControllerTest, MinimizeFullscreenWindow) { 301 // Two windows, w1 normal, w2 fullscreen. 302 scoped_ptr<Window> w1(CreateTestWindow()); 303 scoped_ptr<Window> w2(CreateTestWindow()); 304 w1->Show(); 305 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 306 w2->Show(); 307 308 wm::WindowState* w1_state = wm::GetWindowState(w1.get()); 309 wm::WindowState* w2_state = wm::GetWindowState(w2.get()); 310 311 w2_state->Activate(); 312 313 // Minimize w2. 314 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 315 EXPECT_TRUE(w1->layer()->IsDrawn()); 316 EXPECT_FALSE(w2->layer()->IsDrawn()); 317 318 // Show the window, which should trigger unminimizing. 319 w2->Show(); 320 w2_state->Activate(); 321 322 EXPECT_TRUE(w2_state->IsFullscreen()); 323 EXPECT_TRUE(w1->layer()->IsDrawn()); 324 EXPECT_TRUE(w2->layer()->IsDrawn()); 325 326 // Minimize the window, which should hide the window. 327 EXPECT_TRUE(w2_state->IsActive()); 328 w2_state->Minimize(); 329 EXPECT_FALSE(w2_state->IsActive()); 330 EXPECT_FALSE(w2->layer()->IsDrawn()); 331 EXPECT_TRUE(w1_state->IsActive()); 332 333 // Make the window normal. 334 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 335 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 336 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]); 337 EXPECT_TRUE(w2->layer()->IsDrawn()); 338} 339 340// Verifies ShelfLayoutManager's visibility/auto-hide state is correctly 341// updated. 342TEST_F(WorkspaceControllerTest, ShelfStateUpdated) { 343 // Since ShelfLayoutManager queries for mouse location, move the mouse so 344 // it isn't over the shelf. 345 aura::test::EventGenerator generator( 346 Shell::GetPrimaryRootWindow(), gfx::Point()); 347 generator.MoveMouseTo(0, 0); 348 349 scoped_ptr<Window> w1(CreateTestWindow()); 350 const gfx::Rect w1_bounds(0, 1, 101, 102); 351 ShelfLayoutManager* shelf = shelf_layout_manager(); 352 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); 353 const gfx::Rect touches_shelf_bounds( 354 0, shelf->GetIdealBounds().y() - 10, 101, 102); 355 // Move |w1| to overlap the shelf. 356 w1->SetBounds(touches_shelf_bounds); 357 EXPECT_FALSE(GetWindowOverlapsShelf()); 358 359 // A visible ignored window should not trigger the overlap. 360 scoped_ptr<Window> w_ignored(CreateTestWindow()); 361 w_ignored->SetBounds(touches_shelf_bounds); 362 wm::GetWindowState(&(*w_ignored))->set_ignored_by_shelf(true); 363 w_ignored->Show(); 364 EXPECT_FALSE(GetWindowOverlapsShelf()); 365 366 // Make it visible, since visible shelf overlaps should be true. 367 w1->Show(); 368 EXPECT_TRUE(GetWindowOverlapsShelf()); 369 370 wm::ActivateWindow(w1.get()); 371 w1->SetBounds(w1_bounds); 372 w1->Show(); 373 wm::ActivateWindow(w1.get()); 374 375 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 376 377 // Maximize the window. 378 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 379 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 380 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 381 382 // Restore. 383 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 384 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 385 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 386 387 // Fullscreen. 388 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 389 EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state()); 390 391 // Normal. 392 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 393 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 394 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 395 EXPECT_FALSE(GetWindowOverlapsShelf()); 396 397 // Move window so it obscures shelf. 398 w1->SetBounds(touches_shelf_bounds); 399 EXPECT_TRUE(GetWindowOverlapsShelf()); 400 401 // Move it back. 402 w1->SetBounds(w1_bounds); 403 EXPECT_FALSE(GetWindowOverlapsShelf()); 404 405 // Maximize again. 406 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 407 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 408 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 409 410 // Minimize. 411 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 412 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 413 414 // Since the restore from minimize will restore to the pre-minimize 415 // state (tested elsewhere), we abandon the current size and restore 416 // rect and set them to the window. 417 wm::WindowState* window_state = wm::GetWindowState(w1.get()); 418 419 gfx::Rect restore = window_state->GetRestoreBoundsInScreen(); 420 EXPECT_EQ("0,0 800x597", w1->bounds().ToString()); 421 EXPECT_EQ("0,1 101x102", restore.ToString()); 422 window_state->ClearRestoreBounds(); 423 w1->SetBounds(restore); 424 425 // Restore. 426 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 427 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 428 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 429 430 // Create another window, maximized. 431 scoped_ptr<Window> w2(CreateTestWindow()); 432 w2->SetBounds(gfx::Rect(10, 11, 250, 251)); 433 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 434 w2->Show(); 435 wm::ActivateWindow(w2.get()); 436 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 437 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 438 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 439 440 // Switch to w1. 441 wm::ActivateWindow(w1.get()); 442 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 443 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 444 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent( 445 w2->parent()).ToString(), 446 w2->bounds().ToString()); 447 448 // Switch to w2. 449 wm::ActivateWindow(w2.get()); 450 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 451 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 452 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 453 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w2.get()).ToString(), 454 w2->bounds().ToString()); 455 456 // Turn off auto-hide, switch back to w2 (maximized) and verify overlap. 457 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); 458 wm::ActivateWindow(w2.get()); 459 EXPECT_FALSE(GetWindowOverlapsShelf()); 460 461 // Move w1 to overlap shelf, it shouldn't change window overlaps shelf since 462 // the window isn't in the visible workspace. 463 w1->SetBounds(touches_shelf_bounds); 464 EXPECT_FALSE(GetWindowOverlapsShelf()); 465 466 // Activate w1. Although w1 is visible, the overlap state is still false since 467 // w2 is maximized. 468 wm::ActivateWindow(w1.get()); 469 EXPECT_FALSE(GetWindowOverlapsShelf()); 470 471 // Restore w2. 472 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 473 EXPECT_TRUE(GetWindowOverlapsShelf()); 474} 475 476// Verifies going from maximized to minimized sets the right state for painting 477// the background of the launcher. 478TEST_F(WorkspaceControllerTest, MinimizeResetsVisibility) { 479 scoped_ptr<Window> w1(CreateTestWindow()); 480 w1->Show(); 481 wm::ActivateWindow(w1.get()); 482 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 483 EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, shelf_widget()->GetBackgroundType()); 484 485 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 486 EXPECT_EQ(SHELF_VISIBLE, 487 shelf_layout_manager()->visibility_state()); 488 EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, shelf_widget()->GetBackgroundType()); 489} 490 491// Verifies window visibility during various workspace changes. 492TEST_F(WorkspaceControllerTest, VisibilityTests) { 493 scoped_ptr<Window> w1(CreateTestWindow()); 494 w1->Show(); 495 EXPECT_TRUE(w1->IsVisible()); 496 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity()); 497 498 // Create another window, activate it and make it fullscreen. 499 scoped_ptr<Window> w2(CreateTestWindow()); 500 w2->Show(); 501 wm::ActivateWindow(w2.get()); 502 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 503 EXPECT_TRUE(w2->IsVisible()); 504 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity()); 505 EXPECT_TRUE(w1->IsVisible()); 506 507 // Switch to w1. |w1| should be visible on top of |w2|. 508 wm::ActivateWindow(w1.get()); 509 EXPECT_TRUE(w1->IsVisible()); 510 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity()); 511 EXPECT_TRUE(w2->IsVisible()); 512 513 // Switch back to |w2|. 514 wm::ActivateWindow(w2.get()); 515 EXPECT_TRUE(w2->IsVisible()); 516 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity()); 517 EXPECT_TRUE(w1->IsVisible()); 518 519 // Restore |w2|, both windows should be visible. 520 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 521 EXPECT_TRUE(w1->IsVisible()); 522 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity()); 523 EXPECT_TRUE(w2->IsVisible()); 524 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity()); 525 526 // Make |w2| fullscreen again, then close it. 527 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 528 w2->Hide(); 529 EXPECT_FALSE(w2->IsVisible()); 530 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity()); 531 EXPECT_TRUE(w1->IsVisible()); 532 533 // Create |w2| and maximize it. 534 w2.reset(CreateTestWindow()); 535 w2->Show(); 536 wm::ActivateWindow(w2.get()); 537 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 538 EXPECT_TRUE(w2->IsVisible()); 539 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity()); 540 EXPECT_TRUE(w1->IsVisible()); 541 542 // Close |w2|. 543 w2.reset(); 544 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity()); 545 EXPECT_TRUE(w1->IsVisible()); 546} 547 548// Verifies windows that are offscreen don't move when switching workspaces. 549TEST_F(WorkspaceControllerTest, DontMoveOnSwitch) { 550 aura::test::EventGenerator generator( 551 Shell::GetPrimaryRootWindow(), gfx::Point()); 552 generator.MoveMouseTo(0, 0); 553 554 scoped_ptr<Window> w1(CreateTestWindow()); 555 ShelfLayoutManager* shelf = shelf_layout_manager(); 556 const gfx::Rect touches_shelf_bounds( 557 0, shelf->GetIdealBounds().y() - 10, 101, 102); 558 // Move |w1| to overlap the shelf. 559 w1->SetBounds(touches_shelf_bounds); 560 w1->Show(); 561 wm::ActivateWindow(w1.get()); 562 563 // Create another window and maximize it. 564 scoped_ptr<Window> w2(CreateTestWindow()); 565 w2->SetBounds(gfx::Rect(10, 11, 250, 251)); 566 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 567 w2->Show(); 568 wm::ActivateWindow(w2.get()); 569 570 // Switch to w1. 571 wm::ActivateWindow(w1.get()); 572 EXPECT_EQ(touches_shelf_bounds.ToString(), w1->bounds().ToString()); 573} 574 575// Verifies that windows that are completely offscreen move when switching 576// workspaces. 577TEST_F(WorkspaceControllerTest, MoveOnSwitch) { 578 aura::test::EventGenerator generator( 579 Shell::GetPrimaryRootWindow(), gfx::Point()); 580 generator.MoveMouseTo(0, 0); 581 582 scoped_ptr<Window> w1(CreateTestWindow()); 583 ShelfLayoutManager* shelf = shelf_layout_manager(); 584 const gfx::Rect w1_bounds(0, shelf->GetIdealBounds().y(), 100, 200); 585 // Move |w1| so that the top edge is the same as the top edge of the shelf. 586 w1->SetBounds(w1_bounds); 587 w1->Show(); 588 wm::ActivateWindow(w1.get()); 589 EXPECT_EQ(w1_bounds.ToString(), w1->bounds().ToString()); 590 591 // Create another window and maximize it. 592 scoped_ptr<Window> w2(CreateTestWindow()); 593 w2->SetBounds(gfx::Rect(10, 11, 250, 251)); 594 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 595 w2->Show(); 596 wm::ActivateWindow(w2.get()); 597 598 // Increase the size of the WorkAreaInsets. This would make |w1| fall 599 // completely out of the display work area. 600 gfx::Insets insets = 601 Shell::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets(); 602 insets.Set(0, 0, insets.bottom() + 30, 0); 603 Shell::GetInstance()->SetDisplayWorkAreaInsets(w1.get(), insets); 604 605 // Switch to w1. The window should have moved. 606 wm::ActivateWindow(w1.get()); 607 EXPECT_NE(w1_bounds.ToString(), w1->bounds().ToString()); 608} 609 610namespace { 611 612// WindowDelegate used by DontCrashOnChangeAndActivate. 613class DontCrashOnChangeAndActivateDelegate 614 : public aura::test::TestWindowDelegate { 615 public: 616 DontCrashOnChangeAndActivateDelegate() : window_(NULL) {} 617 618 void set_window(aura::Window* window) { window_ = window; } 619 620 // WindowDelegate overrides: 621 virtual void OnBoundsChanged(const gfx::Rect& old_bounds, 622 const gfx::Rect& new_bounds) OVERRIDE { 623 if (window_) { 624 wm::ActivateWindow(window_); 625 window_ = NULL; 626 } 627 } 628 629 private: 630 aura::Window* window_; 631 632 DISALLOW_COPY_AND_ASSIGN(DontCrashOnChangeAndActivateDelegate); 633}; 634 635} // namespace 636 637// Exercises possible crash in W2. Here's the sequence: 638// . minimize a maximized window. 639// . remove the window (which happens when switching displays). 640// . add the window back. 641// . show the window and during the bounds change activate it. 642TEST_F(WorkspaceControllerTest, DontCrashOnChangeAndActivate) { 643 // Force the shelf 644 ShelfLayoutManager* shelf = shelf_layout_manager(); 645 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); 646 647 DontCrashOnChangeAndActivateDelegate delegate; 648 scoped_ptr<Window> w1(CreateTestWindowInShellWithDelegate( 649 &delegate, 1000, gfx::Rect(10, 11, 250, 251))); 650 651 w1->Show(); 652 wm::WindowState* w1_state = wm::GetWindowState(w1.get()); 653 w1_state->Activate(); 654 w1_state->Maximize(); 655 w1_state->Minimize(); 656 657 w1->parent()->RemoveChild(w1.get()); 658 659 // Do this so that when we Show() the window a resize occurs and we make the 660 // window active. 661 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); 662 663 ParentWindowInPrimaryRootWindow(w1.get()); 664 delegate.set_window(w1.get()); 665 w1->Show(); 666} 667 668// Verifies a window with a transient parent not managed by workspace works. 669TEST_F(WorkspaceControllerTest, TransientParent) { 670 // Normal window with no transient parent. 671 scoped_ptr<Window> w2(CreateTestWindow()); 672 w2->SetBounds(gfx::Rect(10, 11, 250, 251)); 673 w2->Show(); 674 wm::ActivateWindow(w2.get()); 675 676 // Window with a transient parent. We set the transient parent to the root, 677 // which would never happen but is enough to exercise the bug. 678 scoped_ptr<Window> w1(CreateTestWindowUnparented()); 679 Shell::GetInstance()->GetPrimaryRootWindow()->AddTransientChild(w1.get()); 680 w1->SetBounds(gfx::Rect(10, 11, 250, 251)); 681 ParentWindowInPrimaryRootWindow(w1.get()); 682 w1->Show(); 683 wm::ActivateWindow(w1.get()); 684 685 // The window with the transient parent should get added to the same parent as 686 // the normal window. 687 EXPECT_EQ(w2->parent(), w1->parent()); 688} 689 690// Test the placement of newly created windows. 691TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) { 692 if (!SupportsHostWindowResize()) 693 return; 694 UpdateDisplay("1600x1200"); 695 // Creating a popup handler here to make sure it does not interfere with the 696 // existing windows. 697 gfx::Rect source_browser_bounds(16, 32, 640, 320); 698 scoped_ptr<aura::Window> browser_window(CreateBrowserLikeWindow( 699 source_browser_bounds)); 700 701 // Creating a popup to make sure it does not interfere with the positioning. 702 scoped_ptr<aura::Window> browser_popup(CreatePopupLikeWindow( 703 gfx::Rect(16, 32, 128, 256))); 704 705 browser_window->Show(); 706 browser_popup->Show(); 707 708 { // With a shown window it's size should get returned. 709 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow( 710 source_browser_bounds)); 711 // The position should be right flush. 712 EXPECT_EQ("960,32 640x320", new_browser_window->bounds().ToString()); 713 } 714 715 { // With the window shown - but more on the right side then on the left 716 // side (and partially out of the screen), it should default to the other 717 // side and inside the screen. 718 gfx::Rect source_browser_bounds(gfx::Rect(1000, 600, 640, 320)); 719 browser_window->SetBounds(source_browser_bounds); 720 721 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow( 722 source_browser_bounds)); 723 // The position should be left & bottom flush. 724 EXPECT_EQ("0,600 640x320", new_browser_window->bounds().ToString()); 725 726 // If the other window was already beyond the point to get right flush 727 // it will remain where it is. 728 EXPECT_EQ("1000,600 640x320", browser_window->bounds().ToString()); 729 } 730 731 { // Make sure that popups do not get changed. 732 scoped_ptr<aura::Window> new_popup_window(CreatePopupLikeWindow( 733 gfx::Rect(50, 100, 300, 150))); 734 EXPECT_EQ("50,100 300x150", new_popup_window->bounds().ToString()); 735 } 736 737 browser_window->Hide(); 738 { // If a window is there but not shown the default should be centered. 739 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow( 740 gfx::Rect(50, 100, 300, 150))); 741 EXPECT_EQ("650,100 300x150", new_browser_window->bounds().ToString()); 742 } 743} 744 745// Test the basic auto placement of one and or two windows in a "simulated 746// session" of sequential window operations. 747TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnShowHide) { 748 // Test 1: In case there is no manageable window, no window should shift. 749 750 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 751 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 752 gfx::Rect desktop_area = window1->parent()->bounds(); 753 754 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 755 // Trigger the auto window placement function by making it visible. 756 // Note that the bounds are getting changed while it is invisible. 757 window2->Hide(); 758 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 759 window2->Show(); 760 761 // Check the initial position of the windows is unchanged. 762 EXPECT_EQ("16,32 640x320", window1->bounds().ToString()); 763 EXPECT_EQ("32,48 256x512", window2->bounds().ToString()); 764 765 // Remove the second window and make sure that the first window 766 // does NOT get centered. 767 window2.reset(); 768 EXPECT_EQ("16,32 640x320", window1->bounds().ToString()); 769 770 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 771 // Test 2: Set up two managed windows and check their auto positioning. 772 window1_state->set_window_position_managed(true); 773 774 scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2)); 775 wm::GetWindowState(window3.get())->set_window_position_managed(true); 776 // To avoid any auto window manager changes due to SetBounds, the window 777 // gets first hidden and then shown again. 778 window3->Hide(); 779 window3->SetBounds(gfx::Rect(32, 48, 256, 512)); 780 window3->Show(); 781 // |window1| should be flush left and |window3| flush right. 782 EXPECT_EQ("0,32 640x320", window1->bounds().ToString()); 783 EXPECT_EQ(base::IntToString( 784 desktop_area.width() - window3->bounds().width()) + 785 ",48 256x512", window3->bounds().ToString()); 786 787 // After removing |window3|, |window1| should be centered again. 788 window3.reset(); 789 EXPECT_EQ( 790 base::IntToString( 791 (desktop_area.width() - window1->bounds().width()) / 2) + 792 ",32 640x320", window1->bounds().ToString()); 793 794 // Test 3: Set up a manageable and a non manageable window and check 795 // positioning. 796 scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(3)); 797 // To avoid any auto window manager changes due to SetBounds, the window 798 // gets first hidden and then shown again. 799 window1->Hide(); 800 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 801 window4->SetBounds(gfx::Rect(32, 48, 256, 512)); 802 window1->Show(); 803 // |window1| should be centered and |window4| untouched. 804 EXPECT_EQ( 805 base::IntToString( 806 (desktop_area.width() - window1->bounds().width()) / 2) + 807 ",32 640x320", window1->bounds().ToString()); 808 EXPECT_EQ("32,48 256x512", window4->bounds().ToString()); 809 810 // Test4: A single manageable window should get centered. 811 window4.reset(); 812 window1_state->set_bounds_changed_by_user(false); 813 // Trigger the auto window placement function by showing (and hiding) it. 814 window1->Hide(); 815 window1->Show(); 816 // |window1| should be centered. 817 EXPECT_EQ( 818 base::IntToString( 819 (desktop_area.width() - window1->bounds().width()) / 2) + 820 ",32 640x320", window1->bounds().ToString()); 821} 822 823// Test the proper usage of user window movement interaction. 824TEST_F(WorkspaceControllerTest, TestUserMovedWindowRepositioning) { 825 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 826 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 827 gfx::Rect desktop_area = window1->parent()->bounds(); 828 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 829 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 830 window1->Hide(); 831 window2->Hide(); 832 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 833 wm::WindowState* window2_state = wm::GetWindowState(window2.get()); 834 835 window1_state->set_window_position_managed(true); 836 window2_state->set_window_position_managed(true); 837 EXPECT_FALSE(window1_state->bounds_changed_by_user()); 838 EXPECT_FALSE(window2_state->bounds_changed_by_user()); 839 840 // Check that the current location gets preserved if the user has 841 // positioned it previously. 842 window1_state->set_bounds_changed_by_user(true); 843 window1->Show(); 844 EXPECT_EQ("16,32 640x320", window1->bounds().ToString()); 845 // Flag should be still set. 846 EXPECT_TRUE(window1_state->bounds_changed_by_user()); 847 EXPECT_FALSE(window2_state->bounds_changed_by_user()); 848 849 // Turn on the second window and make sure that both windows are now 850 // positionable again (user movement cleared). 851 window2->Show(); 852 853 // |window1| should be flush left and |window2| flush right. 854 EXPECT_EQ("0,32 640x320", window1->bounds().ToString()); 855 EXPECT_EQ( 856 base::IntToString(desktop_area.width() - window2->bounds().width()) + 857 ",48 256x512", window2->bounds().ToString()); 858 // FLag should now be reset. 859 EXPECT_FALSE(window1_state->bounds_changed_by_user()); 860 EXPECT_FALSE(window2_state->bounds_changed_by_user()); 861 862 // Going back to one shown window should keep the state. 863 window1_state->set_bounds_changed_by_user(true); 864 window2->Hide(); 865 EXPECT_EQ("0,32 640x320", window1->bounds().ToString()); 866 EXPECT_TRUE(window1_state->bounds_changed_by_user()); 867} 868 869// Test if the single window will be restored at original position. 870TEST_F(WorkspaceControllerTest, TestSingleWindowsRestoredBounds) { 871 scoped_ptr<aura::Window> window1( 872 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); 873 scoped_ptr<aura::Window> window2( 874 CreateTestWindowInShellWithBounds(gfx::Rect(110, 110, 100, 100))); 875 scoped_ptr<aura::Window> window3( 876 CreateTestWindowInShellWithBounds(gfx::Rect(120, 120, 100, 100))); 877 window1->Hide(); 878 window2->Hide(); 879 window3->Hide(); 880 wm::GetWindowState(window1.get())->set_window_position_managed(true); 881 wm::GetWindowState(window2.get())->set_window_position_managed(true); 882 wm::GetWindowState(window3.get())->set_window_position_managed(true); 883 884 window1->Show(); 885 wm::ActivateWindow(window1.get()); 886 window2->Show(); 887 wm::ActivateWindow(window2.get()); 888 window3->Show(); 889 wm::ActivateWindow(window3.get()); 890 EXPECT_EQ(0, window1->bounds().x()); 891 EXPECT_EQ(window2->GetRootWindow()->bounds().right(), 892 window2->bounds().right()); 893 EXPECT_EQ(0, window3->bounds().x()); 894 895 window1->Hide(); 896 EXPECT_EQ(window2->GetRootWindow()->bounds().right(), 897 window2->bounds().right()); 898 EXPECT_EQ(0, window3->bounds().x()); 899 900 // Being a single window will retore the original location. 901 window3->Hide(); 902 wm::ActivateWindow(window2.get()); 903 EXPECT_EQ("110,110 100x100", window2->bounds().ToString()); 904 905 // Showing the 3rd will push the 2nd window left. 906 window3->Show(); 907 wm::ActivateWindow(window3.get()); 908 EXPECT_EQ(0, window2->bounds().x()); 909 EXPECT_EQ(window3->GetRootWindow()->bounds().right(), 910 window3->bounds().right()); 911 912 // Being a single window will retore the original location. 913 window2->Hide(); 914 EXPECT_EQ("120,120 100x100", window3->bounds().ToString()); 915} 916 917// Test that user placed windows go back to their user placement after the user 918// closes all other windows. 919TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) { 920 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 921 gfx::Rect user_pos = gfx::Rect(16, 42, 640, 320); 922 window1->SetBounds(user_pos); 923 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 924 925 window1_state->SetPreAutoManageWindowBounds(user_pos); 926 gfx::Rect desktop_area = window1->parent()->bounds(); 927 928 // Create a second window to let the auto manager kick in. 929 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 930 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 931 window1->Hide(); 932 window2->Hide(); 933 wm::GetWindowState(window1.get())->set_window_position_managed(true); 934 wm::GetWindowState(window2.get())->set_window_position_managed(true); 935 window1->Show(); 936 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString()); 937 window2->Show(); 938 939 // |window1| should be flush left and |window2| flush right. 940 EXPECT_EQ("0," + base::IntToString(user_pos.y()) + 941 " 640x320", window1->bounds().ToString()); 942 EXPECT_EQ( 943 base::IntToString(desktop_area.width() - window2->bounds().width()) + 944 ",48 256x512", window2->bounds().ToString()); 945 window2->Hide(); 946 947 // After the other window get hidden the window has to move back to the 948 // previous position and the bounds should still be set and unchanged. 949 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString()); 950 ASSERT_TRUE(window1_state->pre_auto_manage_window_bounds()); 951 EXPECT_EQ(user_pos.ToString(), 952 window1_state->pre_auto_manage_window_bounds()->ToString()); 953} 954 955// Test that a window from normal to minimize will repos the remaining. 956TEST_F(WorkspaceControllerTest, ToMinimizeRepositionsRemaining) { 957 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 958 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 959 window1_state->set_window_position_managed(true); 960 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 961 gfx::Rect desktop_area = window1->parent()->bounds(); 962 963 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 964 wm::WindowState* window2_state = wm::GetWindowState(window2.get()); 965 window2_state->set_window_position_managed(true); 966 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 967 968 window1_state->Minimize(); 969 970 // |window2| should be centered now. 971 EXPECT_TRUE(window2->IsVisible()); 972 EXPECT_TRUE(window2_state->IsNormalShowState()); 973 EXPECT_EQ(base::IntToString( 974 (desktop_area.width() - window2->bounds().width()) / 2) + 975 ",48 256x512", window2->bounds().ToString()); 976 977 window1_state->Restore(); 978 // |window1| should be flush right and |window3| flush left. 979 EXPECT_EQ(base::IntToString( 980 desktop_area.width() - window1->bounds().width()) + 981 ",32 640x320", window1->bounds().ToString()); 982 EXPECT_EQ("0,48 256x512", window2->bounds().ToString()); 983} 984 985// Test that minimizing an initially maximized window will repos the remaining. 986TEST_F(WorkspaceControllerTest, MaxToMinRepositionsRemaining) { 987 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 988 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 989 window1_state->set_window_position_managed(true); 990 gfx::Rect desktop_area = window1->parent()->bounds(); 991 992 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 993 wm::WindowState* window2_state = wm::GetWindowState(window2.get()); 994 window2_state->set_window_position_managed(true); 995 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 996 997 window1_state->Maximize(); 998 window1_state->Minimize(); 999 1000 // |window2| should be centered now. 1001 EXPECT_TRUE(window2->IsVisible()); 1002 EXPECT_TRUE(window2_state->IsNormalShowState()); 1003 EXPECT_EQ(base::IntToString( 1004 (desktop_area.width() - window2->bounds().width()) / 2) + 1005 ",48 256x512", window2->bounds().ToString()); 1006} 1007 1008// Test that nomral, maximize, minimizing will repos the remaining. 1009TEST_F(WorkspaceControllerTest, NormToMaxToMinRepositionsRemaining) { 1010 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 1011 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 1012 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 1013 window1_state->set_window_position_managed(true); 1014 gfx::Rect desktop_area = window1->parent()->bounds(); 1015 1016 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 1017 wm::WindowState* window2_state = wm::GetWindowState(window2.get()); 1018 window2_state->set_window_position_managed(true); 1019 window2->SetBounds(gfx::Rect(32, 40, 256, 512)); 1020 1021 // Trigger the auto window placement function by showing (and hiding) it. 1022 window1->Hide(); 1023 window1->Show(); 1024 1025 // |window1| should be flush right and |window3| flush left. 1026 EXPECT_EQ(base::IntToString( 1027 desktop_area.width() - window1->bounds().width()) + 1028 ",32 640x320", window1->bounds().ToString()); 1029 EXPECT_EQ("0,40 256x512", window2->bounds().ToString()); 1030 1031 window1_state->Maximize(); 1032 window1_state->Minimize(); 1033 1034 // |window2| should be centered now. 1035 EXPECT_TRUE(window2->IsVisible()); 1036 EXPECT_TRUE(window2_state->IsNormalShowState()); 1037 EXPECT_EQ(base::IntToString( 1038 (desktop_area.width() - window2->bounds().width()) / 2) + 1039 ",40 256x512", window2->bounds().ToString()); 1040} 1041 1042// Test that nomral, maximize, normal will repos the remaining. 1043TEST_F(WorkspaceControllerTest, NormToMaxToNormRepositionsRemaining) { 1044 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 1045 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 1046 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 1047 window1_state->set_window_position_managed(true); 1048 gfx::Rect desktop_area = window1->parent()->bounds(); 1049 1050 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 1051 wm::GetWindowState(window2.get())->set_window_position_managed(true); 1052 window2->SetBounds(gfx::Rect(32, 40, 256, 512)); 1053 1054 // Trigger the auto window placement function by showing (and hiding) it. 1055 window1->Hide(); 1056 window1->Show(); 1057 1058 // |window1| should be flush right and |window3| flush left. 1059 EXPECT_EQ(base::IntToString( 1060 desktop_area.width() - window1->bounds().width()) + 1061 ",32 640x320", window1->bounds().ToString()); 1062 EXPECT_EQ("0,40 256x512", window2->bounds().ToString()); 1063 1064 window1_state->Maximize(); 1065 window1_state->Restore(); 1066 1067 // |window1| should be flush right and |window2| flush left. 1068 EXPECT_EQ(base::IntToString( 1069 desktop_area.width() - window1->bounds().width()) + 1070 ",32 640x320", window1->bounds().ToString()); 1071 EXPECT_EQ("0,40 256x512", window2->bounds().ToString()); 1072} 1073 1074// Test that animations are triggered. 1075TEST_F(WorkspaceControllerTest, AnimatedNormToMaxToNormRepositionsRemaining) { 1076 ui::ScopedAnimationDurationScaleMode normal_duration_mode( 1077 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); 1078 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 1079 window1->Hide(); 1080 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 1081 gfx::Rect desktop_area = window1->parent()->bounds(); 1082 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 1083 window2->Hide(); 1084 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 1085 1086 wm::GetWindowState(window1.get())->set_window_position_managed(true); 1087 wm::GetWindowState(window2.get())->set_window_position_managed(true); 1088 // Make sure nothing is animating. 1089 window1->layer()->GetAnimator()->StopAnimating(); 1090 window2->layer()->GetAnimator()->StopAnimating(); 1091 window2->Show(); 1092 1093 // The second window should now animate. 1094 EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating()); 1095 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating()); 1096 window2->layer()->GetAnimator()->StopAnimating(); 1097 1098 window1->Show(); 1099 EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating()); 1100 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating()); 1101 1102 window1->layer()->GetAnimator()->StopAnimating(); 1103 window2->layer()->GetAnimator()->StopAnimating(); 1104 // |window1| should be flush right and |window2| flush left. 1105 EXPECT_EQ(base::IntToString( 1106 desktop_area.width() - window1->bounds().width()) + 1107 ",32 640x320", window1->bounds().ToString()); 1108 EXPECT_EQ("0,48 256x512", window2->bounds().ToString()); 1109} 1110 1111// This tests simulates a browser and an app and verifies the ordering of the 1112// windows and layers doesn't get out of sync as various operations occur. Its 1113// really testing code in FocusController, but easier to simulate here. Just as 1114// with a real browser the browser here has a transient child window 1115// (corresponds to the status bubble). 1116TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) { 1117 scoped_ptr<Window> browser( 1118 aura::test::CreateTestWindowWithDelegate( 1119 NULL, 1120 aura::client::WINDOW_TYPE_NORMAL, 1121 gfx::Rect(5, 6, 7, 8), 1122 NULL)); 1123 browser->SetName("browser"); 1124 ParentWindowInPrimaryRootWindow(browser.get()); 1125 browser->Show(); 1126 wm::ActivateWindow(browser.get()); 1127 1128 // |status_bubble| is made a transient child of |browser| and as a result 1129 // owned by |browser|. 1130 aura::test::TestWindowDelegate* status_bubble_delegate = 1131 aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(); 1132 status_bubble_delegate->set_can_focus(false); 1133 Window* status_bubble = 1134 aura::test::CreateTestWindowWithDelegate( 1135 status_bubble_delegate, 1136 aura::client::WINDOW_TYPE_POPUP, 1137 gfx::Rect(5, 6, 7, 8), 1138 NULL); 1139 browser->AddTransientChild(status_bubble); 1140 ParentWindowInPrimaryRootWindow(status_bubble); 1141 status_bubble->SetName("status_bubble"); 1142 1143 scoped_ptr<Window> app( 1144 aura::test::CreateTestWindowWithDelegate( 1145 NULL, 1146 aura::client::WINDOW_TYPE_NORMAL, 1147 gfx::Rect(5, 6, 7, 8), 1148 NULL)); 1149 app->SetName("app"); 1150 ParentWindowInPrimaryRootWindow(app.get()); 1151 1152 aura::Window* parent = browser->parent(); 1153 1154 app->Show(); 1155 wm::ActivateWindow(app.get()); 1156 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1157 1158 // Minimize the app, focus should go the browser. 1159 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 1160 EXPECT_TRUE(wm::IsActiveWindow(browser.get())); 1161 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1162 1163 // Minimize the browser (neither windows are focused). 1164 browser->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 1165 EXPECT_FALSE(wm::IsActiveWindow(browser.get())); 1166 EXPECT_FALSE(wm::IsActiveWindow(app.get())); 1167 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1168 1169 // Show the browser (which should restore it). 1170 browser->Show(); 1171 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1172 1173 // Activate the browser. 1174 ash::wm::ActivateWindow(browser.get()); 1175 EXPECT_TRUE(wm::IsActiveWindow(browser.get())); 1176 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1177 1178 // Restore the app. This differs from above code for |browser| as internally 1179 // the app code does this. Restoring this way or using Show() should not make 1180 // a difference. 1181 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 1182 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1183 1184 // Activate the app. 1185 ash::wm::ActivateWindow(app.get()); 1186 EXPECT_TRUE(wm::IsActiveWindow(app.get())); 1187 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1188} 1189 1190namespace { 1191 1192// Used by DragMaximizedNonTrackedWindow to track how many times the window 1193// hierarchy changes affecting the specified window. 1194class DragMaximizedNonTrackedWindowObserver 1195 : public aura::WindowObserver { 1196 public: 1197 DragMaximizedNonTrackedWindowObserver(aura::Window* window) 1198 : change_count_(0), 1199 window_(window) { 1200 } 1201 1202 // Number of times OnWindowHierarchyChanged() has been received. 1203 void clear_change_count() { change_count_ = 0; } 1204 int change_count() const { 1205 return change_count_; 1206 } 1207 1208 // aura::WindowObserver overrides: 1209 // Counts number of times a window is reparented. Ignores reparenting into and 1210 // from a docked container which is expected when a tab is dragged. 1211 virtual void OnWindowHierarchyChanged( 1212 const HierarchyChangeParams& params) OVERRIDE { 1213 if (params.target != window_ || 1214 (params.old_parent->id() == kShellWindowId_DefaultContainer && 1215 params.new_parent->id() == kShellWindowId_DockedContainer) || 1216 (params.old_parent->id() == kShellWindowId_DockedContainer && 1217 params.new_parent->id() == kShellWindowId_DefaultContainer)) { 1218 return; 1219 } 1220 change_count_++; 1221 } 1222 1223 private: 1224 int change_count_; 1225 aura::Window* window_; 1226 1227 DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver); 1228}; 1229 1230} // namespace 1231 1232// Verifies that a new maximized window becomes visible after its activation 1233// is requested, even though it does not become activated because a system 1234// modal window is active. 1235TEST_F(WorkspaceControllerTest, SwitchFromModal) { 1236 scoped_ptr<Window> modal_window(CreateTestWindowUnparented()); 1237 modal_window->SetBounds(gfx::Rect(10, 11, 21, 22)); 1238 modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM); 1239 ParentWindowInPrimaryRootWindow(modal_window.get()); 1240 modal_window->Show(); 1241 wm::ActivateWindow(modal_window.get()); 1242 1243 scoped_ptr<Window> maximized_window(CreateTestWindow()); 1244 maximized_window->SetProperty( 1245 aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 1246 maximized_window->Show(); 1247 wm::ActivateWindow(maximized_window.get()); 1248 EXPECT_TRUE(maximized_window->IsVisible()); 1249} 1250 1251namespace { 1252 1253// Subclass of WorkspaceControllerTest that runs tests with docked windows 1254// enabled and disabled. 1255class WorkspaceControllerTestDragging 1256 : public WorkspaceControllerTest, 1257 public testing::WithParamInterface<bool> { 1258 public: 1259 WorkspaceControllerTestDragging() {} 1260 virtual ~WorkspaceControllerTestDragging() {} 1261 1262 // testing::Test: 1263 virtual void SetUp() OVERRIDE { 1264 WorkspaceControllerTest::SetUp(); 1265 if (docked_windows_enabled()) { 1266 CommandLine::ForCurrentProcess()->AppendSwitch( 1267 ash::switches::kAshEnableDockedWindows); 1268 } 1269 } 1270 1271 bool docked_windows_enabled() const { return GetParam(); } 1272 1273 private: 1274 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging); 1275}; 1276 1277} // namespace 1278 1279// Verifies that when dragging a window over the shelf overlap is detected 1280// during and after the drag. 1281TEST_P(WorkspaceControllerTestDragging, DragWindowOverlapShelf) { 1282 aura::test::TestWindowDelegate delegate; 1283 delegate.set_window_component(HTCAPTION); 1284 scoped_ptr<Window> w1( 1285 aura::test::CreateTestWindowWithDelegate(&delegate, 1286 aura::client::WINDOW_TYPE_NORMAL, 1287 gfx::Rect(5, 5, 100, 50), 1288 NULL)); 1289 ParentWindowInPrimaryRootWindow(w1.get()); 1290 1291 ShelfLayoutManager* shelf = shelf_layout_manager(); 1292 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); 1293 1294 // Drag near the shelf 1295 aura::test::EventGenerator generator( 1296 Shell::GetPrimaryRootWindow(), gfx::Point()); 1297 generator.MoveMouseTo(10, 10); 1298 generator.PressLeftButton(); 1299 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 70); 1300 1301 // Shelf should not be in overlapped state. 1302 EXPECT_FALSE(GetWindowOverlapsShelf()); 1303 1304 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20); 1305 1306 // Shelf should detect overlap. Overlap state stays after mouse is released. 1307 EXPECT_TRUE(GetWindowOverlapsShelf()); 1308 generator.ReleaseLeftButton(); 1309 EXPECT_TRUE(GetWindowOverlapsShelf()); 1310} 1311 1312INSTANTIATE_TEST_CASE_P(DockedOrNot, WorkspaceControllerTestDragging, 1313 ::testing::Bool()); 1314 1315} // namespace internal 1316} // namespace ash 1317