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