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 <algorithm> 6 7 #include "ash/accessibility_delegate.h" 8 #include "ash/drag_drop/drag_drop_controller.h" 9 #include "ash/root_window_controller.h" 10 #include "ash/screen_util.h" 11 #include "ash/shelf/shelf.h" 12 #include "ash/shelf/shelf_widget.h" 13 #include "ash/shell.h" 14 #include "ash/shell_window_ids.h" 15 #include "ash/test/ash_test_base.h" 16 #include "ash/test/shelf_test_api.h" 17 #include "ash/test/shelf_view_test_api.h" 18 #include "ash/test/shell_test_api.h" 19 #include "ash/test/test_shelf_delegate.h" 20 #include "ash/wm/mru_window_tracker.h" 21 #include "ash/wm/overview/window_grid.h" 22 #include "ash/wm/overview/window_selector.h" 23 #include "ash/wm/overview/window_selector_controller.h" 24 #include "ash/wm/overview/window_selector_item.h" 25 #include "ash/wm/panels/panel_layout_manager.h" 26 #include "ash/wm/window_state.h" 27 #include "ash/wm/window_util.h" 28 #include "ash/wm/wm_event.h" 29 #include "base/basictypes.h" 30 #include "base/compiler_specific.h" 31 #include "base/memory/scoped_vector.h" 32 #include "base/run_loop.h" 33 #include "base/strings/utf_string_conversions.h" 34 #include "ui/aura/client/aura_constants.h" 35 #include "ui/aura/client/cursor_client.h" 36 #include "ui/aura/client/focus_client.h" 37 #include "ui/aura/test/event_generator.h" 38 #include "ui/aura/test/test_window_delegate.h" 39 #include "ui/aura/test/test_windows.h" 40 #include "ui/aura/window.h" 41 #include "ui/aura/window_event_dispatcher.h" 42 #include "ui/compositor/scoped_animation_duration_scale_mode.h" 43 #include "ui/gfx/rect_conversions.h" 44 #include "ui/gfx/transform.h" 45 #include "ui/views/controls/label.h" 46 #include "ui/views/widget/native_widget_aura.h" 47 #include "ui/views/widget/widget_delegate.h" 48 #include "ui/wm/core/window_util.h" 49 #include "ui/wm/public/activation_delegate.h" 50 51 namespace ash { 52 namespace { 53 54 class NonActivatableActivationDelegate 55 : public aura::client::ActivationDelegate { 56 public: 57 virtual bool ShouldActivate() const OVERRIDE { 58 return false; 59 } 60 }; 61 62 void CancelDrag(DragDropController* controller, bool* canceled) { 63 if (controller->IsDragDropInProgress()) { 64 *canceled = true; 65 controller->DragCancel(); 66 } 67 } 68 69 } // namespace 70 71 class WindowSelectorTest : public test::AshTestBase { 72 public: 73 WindowSelectorTest() {} 74 virtual ~WindowSelectorTest() {} 75 76 virtual void SetUp() OVERRIDE { 77 test::AshTestBase::SetUp(); 78 ASSERT_TRUE(test::TestShelfDelegate::instance()); 79 80 shelf_view_test_.reset(new test::ShelfViewTestAPI( 81 test::ShelfTestAPI(Shelf::ForPrimaryDisplay()).shelf_view())); 82 shelf_view_test_->SetAnimationDuration(1); 83 } 84 85 aura::Window* CreateWindow(const gfx::Rect& bounds) { 86 return CreateTestWindowInShellWithDelegate(&delegate_, -1, bounds); 87 } 88 89 aura::Window* CreateWindowWithId(const gfx::Rect& bounds, int id) { 90 return CreateTestWindowInShellWithDelegate(&delegate_, id, bounds); 91 } 92 aura::Window* CreateNonActivatableWindow(const gfx::Rect& bounds) { 93 aura::Window* window = CreateWindow(bounds); 94 aura::client::SetActivationDelegate(window, 95 &non_activatable_activation_delegate_); 96 EXPECT_FALSE(ash::wm::CanActivateWindow(window)); 97 return window; 98 } 99 100 aura::Window* CreatePanelWindow(const gfx::Rect& bounds) { 101 aura::Window* window = CreateTestWindowInShellWithDelegateAndType( 102 NULL, ui::wm::WINDOW_TYPE_PANEL, 0, bounds); 103 test::TestShelfDelegate::instance()->AddShelfItem(window); 104 shelf_view_test()->RunMessageLoopUntilAnimationsDone(); 105 return window; 106 } 107 108 views::Widget* CreatePanelWindowWidget(const gfx::Rect& bounds) { 109 views::Widget* widget = new views::Widget; 110 views::Widget::InitParams params; 111 params.bounds = bounds; 112 params.type = views::Widget::InitParams::TYPE_PANEL; 113 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 114 widget->Init(params); 115 widget->Show(); 116 ParentWindowInPrimaryRootWindow(widget->GetNativeWindow()); 117 return widget; 118 } 119 120 bool WindowsOverlapping(aura::Window* window1, aura::Window* window2) { 121 gfx::RectF window1_bounds = GetTransformedTargetBounds(window1); 122 gfx::RectF window2_bounds = GetTransformedTargetBounds(window2); 123 return window1_bounds.Intersects(window2_bounds); 124 } 125 126 void ToggleOverview() { 127 ash::Shell::GetInstance()->window_selector_controller()->ToggleOverview(); 128 } 129 130 gfx::Transform GetTransformRelativeTo(gfx::PointF origin, 131 const gfx::Transform& transform) { 132 gfx::Transform t; 133 t.Translate(origin.x(), origin.y()); 134 t.PreconcatTransform(transform); 135 t.Translate(-origin.x(), -origin.y()); 136 return t; 137 } 138 139 gfx::RectF GetTransformedBounds(aura::Window* window) { 140 gfx::RectF bounds(ScreenUtil::ConvertRectToScreen( 141 window->parent(), window->layer()->bounds())); 142 gfx::Transform transform(GetTransformRelativeTo(bounds.origin(), 143 window->layer()->transform())); 144 transform.TransformRect(&bounds); 145 return bounds; 146 } 147 148 gfx::RectF GetTransformedTargetBounds(aura::Window* window) { 149 gfx::RectF bounds(ScreenUtil::ConvertRectToScreen( 150 window->parent(), window->layer()->GetTargetBounds())); 151 gfx::Transform transform(GetTransformRelativeTo(bounds.origin(), 152 window->layer()->GetTargetTransform())); 153 transform.TransformRect(&bounds); 154 return bounds; 155 } 156 157 gfx::RectF GetTransformedBoundsInRootWindow(aura::Window* window) { 158 gfx::RectF bounds = gfx::Rect(window->bounds().size()); 159 aura::Window* root = window->GetRootWindow(); 160 CHECK(window->layer()); 161 CHECK(root->layer()); 162 gfx::Transform transform; 163 if (!window->layer()->GetTargetTransformRelativeTo(root->layer(), 164 &transform)) { 165 return gfx::RectF(); 166 } 167 transform.TransformRect(&bounds); 168 return bounds; 169 } 170 171 void ClickWindow(aura::Window* window) { 172 aura::test::EventGenerator event_generator(window->GetRootWindow(), window); 173 gfx::RectF target = GetTransformedBounds(window); 174 event_generator.ClickLeftButton(); 175 } 176 177 void SendKey(ui::KeyboardCode key) { 178 aura::test::EventGenerator event_generator(Shell::GetPrimaryRootWindow()); 179 event_generator.PressKey(key, 0); 180 event_generator.ReleaseKey(key, 0); 181 } 182 183 bool IsSelecting() { 184 return ash::Shell::GetInstance()->window_selector_controller()-> 185 IsSelecting(); 186 } 187 188 aura::Window* GetFocusedWindow() { 189 return aura::client::GetFocusClient( 190 Shell::GetPrimaryRootWindow())->GetFocusedWindow(); 191 } 192 193 const std::vector<WindowSelectorItem*>& GetWindowItemsForRoot(int index) { 194 return ash::Shell::GetInstance()->window_selector_controller()-> 195 window_selector_->grid_list_[index]->window_list_.get(); 196 } 197 198 const aura::Window* GetSelectedWindow() { 199 WindowSelector* ws = ash::Shell::GetInstance()-> 200 window_selector_controller()->window_selector_.get(); 201 return ws->grid_list_[ws->selected_grid_index_]-> 202 SelectedWindow()->SelectionWindow(); 203 } 204 205 views::Widget* GetCloseButton(ash::WindowSelectorItem* window) { 206 return window->close_button_.get(); 207 } 208 209 views::Widget* GetLabelWidget(ash::WindowSelectorItem* window) { 210 return window->window_label_.get(); 211 } 212 213 // Tests that a window is contained within a given WindowSelectorItem, and 214 // that both the window and its matching close button are within the same 215 // screen. 216 void IsWindowAndCloseButtonInScreen(aura::Window* window, 217 WindowSelectorItem* window_item) { 218 aura::Window* root_window = window_item->GetRootWindow(); 219 EXPECT_TRUE(window_item->Contains(window)); 220 EXPECT_TRUE(root_window->GetBoundsInScreen().Contains( 221 ToEnclosingRect(GetTransformedTargetBounds(window)))); 222 EXPECT_TRUE(root_window->GetBoundsInScreen().Contains( 223 ToEnclosingRect(GetTransformedTargetBounds( 224 GetCloseButton(window_item)->GetNativeView())))); 225 } 226 227 test::ShelfViewTestAPI* shelf_view_test() { 228 return shelf_view_test_.get(); 229 } 230 231 private: 232 aura::test::TestWindowDelegate delegate_; 233 NonActivatableActivationDelegate non_activatable_activation_delegate_; 234 scoped_ptr<test::ShelfViewTestAPI> shelf_view_test_; 235 236 DISALLOW_COPY_AND_ASSIGN(WindowSelectorTest); 237 }; 238 239 // Tests that an a11y alert is sent on entering overview mode. 240 TEST_F(WindowSelectorTest, A11yAlertOnOverviewMode) { 241 gfx::Rect bounds(0, 0, 400, 400); 242 AccessibilityDelegate* delegate = 243 ash::Shell::GetInstance()->accessibility_delegate(); 244 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 245 EXPECT_NE(delegate->GetLastAccessibilityAlert(), 246 A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED); 247 ToggleOverview(); 248 EXPECT_EQ(delegate->GetLastAccessibilityAlert(), 249 A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED); 250 } 251 252 // Tests entering overview mode with two windows and selecting one by clicking. 253 TEST_F(WindowSelectorTest, Basic) { 254 gfx::Rect bounds(0, 0, 400, 400); 255 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 256 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 257 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 258 scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); 259 scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds)); 260 EXPECT_TRUE(WindowsOverlapping(window1.get(), window2.get())); 261 EXPECT_TRUE(WindowsOverlapping(panel1.get(), panel2.get())); 262 wm::ActivateWindow(window2.get()); 263 EXPECT_FALSE(wm::IsActiveWindow(window1.get())); 264 EXPECT_TRUE(wm::IsActiveWindow(window2.get())); 265 EXPECT_EQ(window2.get(), GetFocusedWindow()); 266 // Hide the cursor before entering overview to test that it will be shown. 267 aura::client::GetCursorClient(root_window)->HideCursor(); 268 269 // In overview mode the windows should no longer overlap and focus should 270 // be removed from the window. 271 ToggleOverview(); 272 EXPECT_EQ(NULL, GetFocusedWindow()); 273 EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get())); 274 EXPECT_FALSE(WindowsOverlapping(window1.get(), panel1.get())); 275 // Panels 1 and 2 should still be overlapping being in a single selector 276 // item. 277 EXPECT_TRUE(WindowsOverlapping(panel1.get(), panel2.get())); 278 279 // Clicking window 1 should activate it. 280 ClickWindow(window1.get()); 281 EXPECT_TRUE(wm::IsActiveWindow(window1.get())); 282 EXPECT_FALSE(wm::IsActiveWindow(window2.get())); 283 EXPECT_EQ(window1.get(), GetFocusedWindow()); 284 285 // Cursor should have been unlocked. 286 EXPECT_FALSE(aura::client::GetCursorClient(root_window)->IsCursorLocked()); 287 } 288 289 // Tests selecting a window by tapping on it. 290 TEST_F(WindowSelectorTest, BasicGesture) { 291 gfx::Rect bounds(0, 0, 400, 400); 292 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 293 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 294 wm::ActivateWindow(window1.get()); 295 EXPECT_EQ(window1.get(), GetFocusedWindow()); 296 ToggleOverview(); 297 EXPECT_EQ(NULL, GetFocusedWindow()); 298 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 299 window2.get()); 300 generator.GestureTapAt(gfx::ToEnclosingRect( 301 GetTransformedTargetBounds(window2.get())).CenterPoint()); 302 EXPECT_EQ(window2.get(), GetFocusedWindow()); 303 } 304 305 // Tests that a window does not receive located events when in overview mode. 306 TEST_F(WindowSelectorTest, WindowDoesNotReceiveEvents) { 307 gfx::Rect window_bounds(20, 10, 200, 300); 308 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 309 scoped_ptr<aura::Window> window(CreateWindow(window_bounds)); 310 311 gfx::Point point1(window_bounds.x() + 10, window_bounds.y() + 10); 312 313 ui::MouseEvent event1(ui::ET_MOUSE_PRESSED, point1, point1, 314 ui::EF_NONE, ui::EF_NONE); 315 316 ui::EventTarget* root_target = root_window; 317 ui::EventTargeter* targeter = root_target->GetEventTargeter(); 318 319 // The event should target the window because we are still not in overview 320 // mode. 321 EXPECT_EQ(window, static_cast<aura::Window*>( 322 targeter->FindTargetForEvent(root_target, &event1))); 323 324 ToggleOverview(); 325 326 // The bounds have changed, take that into account. 327 gfx::RectF bounds = GetTransformedBoundsInRootWindow(window.get()); 328 gfx::Point point2(bounds.x() + 10, bounds.y() + 10); 329 ui::MouseEvent event2(ui::ET_MOUSE_PRESSED, point2, point2, 330 ui::EF_NONE, ui::EF_NONE); 331 332 // Now the transparent window should be intercepting this event. 333 EXPECT_NE(window, static_cast<aura::Window*>( 334 targeter->FindTargetForEvent(root_target, &event2))); 335 } 336 337 // Tests that clicking on the close button effectively closes the window. 338 TEST_F(WindowSelectorTest, CloseButton) { 339 scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(200, 300, 250, 450))); 340 341 // We need a widget for the close button the work, a bare window will crash. 342 scoped_ptr<views::Widget> widget(new views::Widget); 343 views::Widget::InitParams params; 344 params.bounds = gfx::Rect(0, 0, 400, 400); 345 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 346 params.parent = window1->parent(); 347 widget->Init(params); 348 widget->Show(); 349 ToggleOverview(); 350 351 aura::Window* window2 = widget->GetNativeWindow(); 352 gfx::RectF bounds = GetTransformedBoundsInRootWindow(window2); 353 gfx::Point point(bounds.top_right().x() - 1, bounds.top_right().y() - 1); 354 aura::test::EventGenerator event_generator(window2->GetRootWindow(), point); 355 356 EXPECT_FALSE(widget->IsClosed()); 357 event_generator.ClickLeftButton(); 358 EXPECT_TRUE(widget->IsClosed()); 359 } 360 361 // Tests entering overview mode with two windows and selecting one. 362 TEST_F(WindowSelectorTest, FullscreenWindow) { 363 gfx::Rect bounds(0, 0, 400, 400); 364 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 365 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 366 scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); 367 wm::ActivateWindow(window1.get()); 368 369 const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN); 370 wm::GetWindowState(window1.get())->OnWMEvent(&toggle_fullscreen_event); 371 // The panel is hidden in fullscreen mode. 372 EXPECT_FALSE(panel1->IsVisible()); 373 EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen()); 374 375 // Enter overview and select the fullscreen window. 376 ToggleOverview(); 377 378 // The panel becomes temporarily visible for the overview. 379 EXPECT_TRUE(panel1->IsVisible()); 380 ClickWindow(window1.get()); 381 382 // The window is still fullscreen as it was selected. The panel should again 383 // be hidden. 384 EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen()); 385 EXPECT_FALSE(panel1->IsVisible()); 386 387 // Entering overview and selecting another window, the previous window remains 388 // fullscreen. 389 // TODO(flackr): Currently the panel remains hidden, but should become visible 390 // again. 391 ToggleOverview(); 392 ClickWindow(window2.get()); 393 EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen()); 394 } 395 396 // Tests that the shelf dimming state is removed while in overview and restored 397 // on exiting overview. 398 TEST_F(WindowSelectorTest, OverviewUndimsShelf) { 399 gfx::Rect bounds(0, 0, 400, 400); 400 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 401 wm::WindowState* window_state = wm::GetWindowState(window1.get()); 402 window_state->Maximize(); 403 ash::ShelfWidget* shelf = Shell::GetPrimaryRootWindowController()->shelf(); 404 EXPECT_TRUE(shelf->GetDimsShelf()); 405 ToggleOverview(); 406 EXPECT_FALSE(shelf->GetDimsShelf()); 407 ToggleOverview(); 408 EXPECT_TRUE(shelf->GetDimsShelf()); 409 } 410 411 // Tests that beginning window selection hides the app list. 412 TEST_F(WindowSelectorTest, SelectingHidesAppList) { 413 gfx::Rect bounds(0, 0, 400, 400); 414 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 415 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 416 Shell::GetInstance()->ToggleAppList(NULL); 417 EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility()); 418 ToggleOverview(); 419 EXPECT_FALSE(Shell::GetInstance()->GetAppListTargetVisibility()); 420 ToggleOverview(); 421 } 422 423 // Tests that a minimized window's visibility and layer visibility is correctly 424 // changed when entering overview and restored when leaving overview mode. 425 // Crashes after the skia roll in http://crrev.com/274114. 426 // http://crbug.com/379570 427 TEST_F(WindowSelectorTest, DISABLED_MinimizedWindowVisibility) { 428 gfx::Rect bounds(0, 0, 400, 400); 429 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 430 wm::WindowState* window_state = wm::GetWindowState(window1.get()); 431 window_state->Minimize(); 432 EXPECT_FALSE(window1->IsVisible()); 433 EXPECT_FALSE(window1->layer()->GetTargetVisibility()); 434 { 435 ui::ScopedAnimationDurationScaleMode normal_duration_mode( 436 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); 437 ToggleOverview(); 438 EXPECT_TRUE(window1->IsVisible()); 439 EXPECT_TRUE(window1->layer()->GetTargetVisibility()); 440 } 441 { 442 ui::ScopedAnimationDurationScaleMode normal_duration_mode( 443 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); 444 ToggleOverview(); 445 EXPECT_FALSE(window1->IsVisible()); 446 EXPECT_FALSE(window1->layer()->GetTargetVisibility()); 447 } 448 } 449 450 // Tests that a bounds change during overview is corrected for. 451 TEST_F(WindowSelectorTest, BoundsChangeDuringOverview) { 452 scoped_ptr<aura::Window> window(CreateWindow(gfx::Rect(0, 0, 400, 400))); 453 ToggleOverview(); 454 gfx::Rect overview_bounds = 455 ToEnclosingRect(GetTransformedTargetBounds(window.get())); 456 window->SetBounds(gfx::Rect(200, 0, 200, 200)); 457 gfx::Rect new_overview_bounds = 458 ToEnclosingRect(GetTransformedTargetBounds(window.get())); 459 EXPECT_EQ(overview_bounds.x(), new_overview_bounds.x()); 460 EXPECT_EQ(overview_bounds.y(), new_overview_bounds.y()); 461 EXPECT_EQ(overview_bounds.width(), new_overview_bounds.width()); 462 EXPECT_EQ(overview_bounds.height(), new_overview_bounds.height()); 463 ToggleOverview(); 464 } 465 466 // Tests that a newly created window aborts overview. 467 TEST_F(WindowSelectorTest, NewWindowCancelsOveriew) { 468 gfx::Rect bounds(0, 0, 400, 400); 469 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 470 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 471 ToggleOverview(); 472 EXPECT_TRUE(IsSelecting()); 473 474 // A window being created should exit overview mode. 475 scoped_ptr<aura::Window> window3(CreateWindow(bounds)); 476 EXPECT_FALSE(IsSelecting()); 477 } 478 479 // Tests that a window activation exits overview mode. 480 TEST_F(WindowSelectorTest, ActivationCancelsOveriew) { 481 gfx::Rect bounds(0, 0, 400, 400); 482 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 483 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 484 window2->Focus(); 485 ToggleOverview(); 486 EXPECT_TRUE(IsSelecting()); 487 488 // A window being activated should exit overview mode. 489 window1->Focus(); 490 EXPECT_FALSE(IsSelecting()); 491 492 // window1 should be focused after exiting even though window2 was focused on 493 // entering overview because we exited due to an activation. 494 EXPECT_EQ(window1.get(), GetFocusedWindow()); 495 } 496 497 // Tests that exiting overview mode without selecting a window restores focus 498 // to the previously focused window. 499 TEST_F(WindowSelectorTest, CancelRestoresFocus) { 500 gfx::Rect bounds(0, 0, 400, 400); 501 scoped_ptr<aura::Window> window(CreateWindow(bounds)); 502 wm::ActivateWindow(window.get()); 503 EXPECT_EQ(window.get(), GetFocusedWindow()); 504 505 // In overview mode, focus should be removed. 506 ToggleOverview(); 507 EXPECT_EQ(NULL, GetFocusedWindow()); 508 509 // If canceling overview mode, focus should be restored. 510 ToggleOverview(); 511 EXPECT_EQ(window.get(), GetFocusedWindow()); 512 } 513 514 // Tests that overview mode is exited if the last remaining window is destroyed. 515 TEST_F(WindowSelectorTest, LastWindowDestroyed) { 516 gfx::Rect bounds(0, 0, 400, 400); 517 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 518 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 519 ToggleOverview(); 520 521 window1.reset(); 522 window2.reset(); 523 EXPECT_FALSE(IsSelecting()); 524 } 525 526 // Tests that entering overview mode restores a window to its original 527 // target location. 528 TEST_F(WindowSelectorTest, QuickReentryRestoresInitialTransform) { 529 gfx::Rect bounds(0, 0, 400, 400); 530 scoped_ptr<aura::Window> window(CreateWindow(bounds)); 531 gfx::Rect initial_bounds = ToEnclosingRect( 532 GetTransformedBounds(window.get())); 533 ToggleOverview(); 534 // Quickly exit and reenter overview mode. The window should still be 535 // animating when we reenter. We cannot short circuit animations for this but 536 // we also don't have to wait for them to complete. 537 { 538 ui::ScopedAnimationDurationScaleMode normal_duration_mode( 539 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); 540 ToggleOverview(); 541 ToggleOverview(); 542 } 543 EXPECT_NE(initial_bounds, ToEnclosingRect( 544 GetTransformedTargetBounds(window.get()))); 545 ToggleOverview(); 546 EXPECT_FALSE(IsSelecting()); 547 EXPECT_EQ(initial_bounds, ToEnclosingRect( 548 GetTransformedTargetBounds(window.get()))); 549 } 550 551 // Tests that non-activatable windows are hidden when entering overview mode. 552 TEST_F(WindowSelectorTest, NonActivatableWindowsHidden) { 553 gfx::Rect bounds(0, 0, 400, 400); 554 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 555 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 556 scoped_ptr<aura::Window> non_activatable_window( 557 CreateNonActivatableWindow(Shell::GetPrimaryRootWindow()->bounds())); 558 EXPECT_TRUE(non_activatable_window->IsVisible()); 559 ToggleOverview(); 560 EXPECT_FALSE(non_activatable_window->IsVisible()); 561 ToggleOverview(); 562 EXPECT_TRUE(non_activatable_window->IsVisible()); 563 564 // Test that a window behind the fullscreen non-activatable window can be 565 // clicked. 566 non_activatable_window->parent()->StackChildAtTop( 567 non_activatable_window.get()); 568 ToggleOverview(); 569 ClickWindow(window1.get()); 570 EXPECT_FALSE(IsSelecting()); 571 EXPECT_TRUE(wm::IsActiveWindow(window1.get())); 572 } 573 574 // Tests that windows with modal child windows are transformed with the modal 575 // child even though not activatable themselves. 576 TEST_F(WindowSelectorTest, ModalChild) { 577 gfx::Rect bounds(0, 0, 400, 400); 578 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 579 scoped_ptr<aura::Window> child1(CreateWindow(bounds)); 580 child1->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); 581 ::wm::AddTransientChild(window1.get(), child1.get()); 582 EXPECT_EQ(window1->parent(), child1->parent()); 583 ToggleOverview(); 584 EXPECT_TRUE(window1->IsVisible()); 585 EXPECT_TRUE(child1->IsVisible()); 586 EXPECT_EQ(ToEnclosingRect(GetTransformedTargetBounds(child1.get())), 587 ToEnclosingRect(GetTransformedTargetBounds(window1.get()))); 588 ToggleOverview(); 589 } 590 591 // Tests that clicking a modal window's parent activates the modal window in 592 // overview. 593 TEST_F(WindowSelectorTest, ClickModalWindowParent) { 594 scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(0, 0, 180, 180))); 595 scoped_ptr<aura::Window> child1(CreateWindow(gfx::Rect(200, 0, 180, 180))); 596 child1->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); 597 ::wm::AddTransientChild(window1.get(), child1.get()); 598 EXPECT_FALSE(WindowsOverlapping(window1.get(), child1.get())); 599 EXPECT_EQ(window1->parent(), child1->parent()); 600 ToggleOverview(); 601 // Given that their relative positions are preserved, the windows should still 602 // not overlap. 603 EXPECT_FALSE(WindowsOverlapping(window1.get(), child1.get())); 604 ClickWindow(window1.get()); 605 EXPECT_FALSE(IsSelecting()); 606 607 // Clicking on window1 should activate child1. 608 EXPECT_TRUE(wm::IsActiveWindow(child1.get())); 609 } 610 611 // Tests that windows remain on the display they are currently on in overview 612 // mode, and that the close buttons are on matching displays. 613 TEST_F(WindowSelectorTest, MultipleDisplays) { 614 if (!SupportsMultipleDisplays()) 615 return; 616 617 UpdateDisplay("600x400,600x400"); 618 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 619 gfx::Rect bounds1(0, 0, 400, 400); 620 gfx::Rect bounds2(650, 0, 400, 400); 621 622 scoped_ptr<aura::Window> window1(CreateWindow(bounds1)); 623 scoped_ptr<aura::Window> window2(CreateWindow(bounds1)); 624 scoped_ptr<aura::Window> window3(CreateWindow(bounds2)); 625 scoped_ptr<aura::Window> window4(CreateWindow(bounds2)); 626 scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds1)); 627 scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds1)); 628 scoped_ptr<aura::Window> panel3(CreatePanelWindow(bounds2)); 629 scoped_ptr<aura::Window> panel4(CreatePanelWindow(bounds2)); 630 EXPECT_EQ(root_windows[0], window1->GetRootWindow()); 631 EXPECT_EQ(root_windows[0], window2->GetRootWindow()); 632 EXPECT_EQ(root_windows[1], window3->GetRootWindow()); 633 EXPECT_EQ(root_windows[1], window4->GetRootWindow()); 634 635 EXPECT_EQ(root_windows[0], panel1->GetRootWindow()); 636 EXPECT_EQ(root_windows[0], panel2->GetRootWindow()); 637 EXPECT_EQ(root_windows[1], panel3->GetRootWindow()); 638 EXPECT_EQ(root_windows[1], panel4->GetRootWindow()); 639 640 // In overview mode, each window remains in the same root window. 641 ToggleOverview(); 642 EXPECT_EQ(root_windows[0], window1->GetRootWindow()); 643 EXPECT_EQ(root_windows[0], window2->GetRootWindow()); 644 EXPECT_EQ(root_windows[1], window3->GetRootWindow()); 645 EXPECT_EQ(root_windows[1], window4->GetRootWindow()); 646 EXPECT_EQ(root_windows[0], panel1->GetRootWindow()); 647 EXPECT_EQ(root_windows[0], panel2->GetRootWindow()); 648 EXPECT_EQ(root_windows[1], panel3->GetRootWindow()); 649 EXPECT_EQ(root_windows[1], panel4->GetRootWindow()); 650 651 const std::vector<WindowSelectorItem*>& primary_window_items = 652 GetWindowItemsForRoot(0); 653 const std::vector<WindowSelectorItem*>& secondary_window_items = 654 GetWindowItemsForRoot(1); 655 656 // Window indices are based on top-down order. The reverse of our creation. 657 IsWindowAndCloseButtonInScreen(window1.get(), primary_window_items[2]); 658 IsWindowAndCloseButtonInScreen(window2.get(), primary_window_items[1]); 659 IsWindowAndCloseButtonInScreen(window3.get(), secondary_window_items[2]); 660 IsWindowAndCloseButtonInScreen(window4.get(), secondary_window_items[1]); 661 662 IsWindowAndCloseButtonInScreen(panel1.get(), primary_window_items[0]); 663 IsWindowAndCloseButtonInScreen(panel2.get(), primary_window_items[0]); 664 IsWindowAndCloseButtonInScreen(panel3.get(), secondary_window_items[0]); 665 IsWindowAndCloseButtonInScreen(panel4.get(), secondary_window_items[0]); 666 667 EXPECT_TRUE(WindowsOverlapping(panel1.get(), panel2.get())); 668 EXPECT_TRUE(WindowsOverlapping(panel3.get(), panel4.get())); 669 EXPECT_FALSE(WindowsOverlapping(panel1.get(), panel3.get())); 670 } 671 672 // Tests shutting down during overview. 673 TEST_F(WindowSelectorTest, Shutdown) { 674 gfx::Rect bounds(0, 0, 400, 400); 675 // These windows will be deleted when the test exits and the Shell instance 676 // is shut down. 677 aura::Window* window1(CreateWindow(bounds)); 678 aura::Window* window2(CreateWindow(bounds)); 679 aura::Window* window3(CreatePanelWindow(bounds)); 680 aura::Window* window4(CreatePanelWindow(bounds)); 681 682 wm::ActivateWindow(window4); 683 wm::ActivateWindow(window3); 684 wm::ActivateWindow(window2); 685 wm::ActivateWindow(window1); 686 687 ToggleOverview(); 688 } 689 690 // Tests removing a display during overview. 691 TEST_F(WindowSelectorTest, RemoveDisplay) { 692 if (!SupportsMultipleDisplays()) 693 return; 694 695 UpdateDisplay("400x400,400x400"); 696 gfx::Rect bounds1(0, 0, 100, 100); 697 gfx::Rect bounds2(450, 0, 100, 100); 698 scoped_ptr<aura::Window> window1(CreateWindow(bounds1)); 699 scoped_ptr<aura::Window> window2(CreateWindow(bounds2)); 700 scoped_ptr<aura::Window> window3(CreatePanelWindow(bounds1)); 701 scoped_ptr<aura::Window> window4(CreatePanelWindow(bounds2)); 702 703 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 704 EXPECT_EQ(root_windows[0], window1->GetRootWindow()); 705 EXPECT_EQ(root_windows[1], window2->GetRootWindow()); 706 EXPECT_EQ(root_windows[0], window3->GetRootWindow()); 707 EXPECT_EQ(root_windows[1], window4->GetRootWindow()); 708 709 wm::ActivateWindow(window4.get()); 710 wm::ActivateWindow(window3.get()); 711 wm::ActivateWindow(window2.get()); 712 wm::ActivateWindow(window1.get()); 713 714 ToggleOverview(); 715 EXPECT_TRUE(IsSelecting()); 716 UpdateDisplay("400x400"); 717 EXPECT_FALSE(IsSelecting()); 718 } 719 720 // Tests starting overview during a drag and drop tracking operation. 721 // TODO(flackr): Fix memory corruption crash when running locally (not failing 722 // on bots). See http://crbug.com/342528. 723 TEST_F(WindowSelectorTest, DISABLED_DragDropInProgress) { 724 bool drag_canceled_by_test = false; 725 gfx::Rect bounds(0, 0, 400, 400); 726 scoped_ptr<aura::Window> window(CreateWindow(bounds)); 727 test::ShellTestApi shell_test_api(Shell::GetInstance()); 728 ash::DragDropController* drag_drop_controller = 729 shell_test_api.drag_drop_controller(); 730 ui::OSExchangeData data; 731 base::MessageLoopForUI::current()->PostTask(FROM_HERE, 732 base::Bind(&WindowSelectorTest::ToggleOverview, 733 base::Unretained(this))); 734 base::MessageLoopForUI::current()->PostTask(FROM_HERE, 735 base::Bind(&CancelDrag, drag_drop_controller, &drag_canceled_by_test)); 736 data.SetString(base::UTF8ToUTF16("I am being dragged")); 737 drag_drop_controller->StartDragAndDrop(data, window->GetRootWindow(), 738 window.get(), gfx::Point(5, 5), ui::DragDropTypes::DRAG_MOVE, 739 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); 740 RunAllPendingInMessageLoop(); 741 EXPECT_FALSE(drag_canceled_by_test); 742 ASSERT_TRUE(IsSelecting()); 743 RunAllPendingInMessageLoop(); 744 } 745 746 // Test that a label is created under the window on entering overview mode. 747 TEST_F(WindowSelectorTest, CreateLabelUnderWindow) { 748 scoped_ptr<aura::Window> window(CreateWindow(gfx::Rect(0, 0, 100, 100))); 749 base::string16 window_title = base::UTF8ToUTF16("My window"); 750 window->set_title(window_title); 751 ToggleOverview(); 752 WindowSelectorItem* window_item = GetWindowItemsForRoot(0).back(); 753 views::Widget* widget = GetLabelWidget(window_item); 754 // Has the label widget been created? 755 ASSERT_TRUE(widget); 756 views::Label* label = static_cast<views::Label*>(widget->GetContentsView()); 757 // Verify the label matches the window title. 758 EXPECT_EQ(label->text(), window_title); 759 // Labels are located based on target_bounds, not the actual window item 760 // bounds. 761 gfx::Rect target_bounds(window_item->target_bounds()); 762 gfx::Rect expected_label_bounds(target_bounds.x(), 763 target_bounds.bottom(), 764 target_bounds.width(), 765 label->GetPreferredSize().height()); 766 gfx::Rect real_label_bounds = widget->GetNativeWindow()->bounds(); 767 EXPECT_EQ(widget->GetNativeWindow()->bounds(), real_label_bounds); 768 } 769 770 // Tests that a label is created for the active panel in a group of panels in 771 // overview mode. 772 TEST_F(WindowSelectorTest, CreateLabelUnderPanel) { 773 scoped_ptr<aura::Window> panel1(CreatePanelWindow(gfx::Rect(0, 0, 100, 100))); 774 scoped_ptr<aura::Window> panel2(CreatePanelWindow(gfx::Rect(0, 0, 100, 100))); 775 base::string16 panel1_title = base::UTF8ToUTF16("My panel"); 776 base::string16 panel2_title = base::UTF8ToUTF16("Another panel"); 777 panel1->set_title(panel1_title); 778 panel2->set_title(panel2_title); 779 wm::ActivateWindow(panel1.get()); 780 ToggleOverview(); 781 WindowSelectorItem* window_item = GetWindowItemsForRoot(0).back(); 782 views::Widget* widget = GetLabelWidget(window_item); 783 // Has the label widget been created? 784 ASSERT_TRUE(widget); 785 views::Label* label = static_cast<views::Label*>(widget->GetContentsView()); 786 // Verify the label matches the active window title. 787 EXPECT_EQ(label->text(), panel1_title); 788 } 789 790 // Tests that overview updates the window positions if the display orientation 791 // changes. 792 TEST_F(WindowSelectorTest, DisplayOrientationChanged) { 793 if (!SupportsHostWindowResize()) 794 return; 795 796 aura::Window* root_window = Shell::GetInstance()->GetPrimaryRootWindow(); 797 UpdateDisplay("600x200"); 798 EXPECT_EQ("0,0 600x200", root_window->bounds().ToString()); 799 gfx::Rect window_bounds(0, 0, 150, 150); 800 ScopedVector<aura::Window> windows; 801 for (int i = 0; i < 3; i++) { 802 windows.push_back(CreateWindow(window_bounds)); 803 } 804 805 ToggleOverview(); 806 for (ScopedVector<aura::Window>::iterator iter = windows.begin(); 807 iter != windows.end(); ++iter) { 808 EXPECT_TRUE(root_window->bounds().Contains( 809 ToEnclosingRect(GetTransformedTargetBounds(*iter)))); 810 } 811 812 // Rotate the display, windows should be repositioned to be within the screen 813 // bounds. 814 UpdateDisplay("600x200/r"); 815 EXPECT_EQ("0,0 200x600", root_window->bounds().ToString()); 816 for (ScopedVector<aura::Window>::iterator iter = windows.begin(); 817 iter != windows.end(); ++iter) { 818 EXPECT_TRUE(root_window->bounds().Contains( 819 ToEnclosingRect(GetTransformedTargetBounds(*iter)))); 820 } 821 } 822 823 // Tests traversing some windows in overview mode with the arrow keys in every 824 // possible direction. 825 TEST_F(WindowSelectorTest, BasicArrowKeyNavigation) { 826 if (!SupportsHostWindowResize()) 827 return; 828 const size_t test_windows = 7; 829 UpdateDisplay("400x300"); 830 ScopedVector<aura::Window> windows; 831 for (size_t i = test_windows; i > 0; i--) 832 windows.push_back(CreateWindowWithId(gfx::Rect(0, 0, 100, 100), i)); 833 834 ui::KeyboardCode arrow_keys[] = { 835 ui::VKEY_RIGHT, 836 ui::VKEY_DOWN, 837 ui::VKEY_LEFT, 838 ui::VKEY_UP 839 }; 840 // Expected window layout: 841 // +-------+ +-------+ +-------+ 842 // | 1 | | 2 | | 3 | 843 // +-------+ +-------+ +-------+ 844 // +-------+ +-------+ +-------+ 845 // | 4 | | 5 | | 6 | 846 // +-------+ +-------+ +-------+ 847 // +-------+ 848 // | 7 | 849 // +-------+ 850 // Index for each window during a full loop plus wrapping around. 851 int index_path_for_direction[][test_windows + 1] = { 852 {1, 2, 3, 4, 5, 6, 7, 1}, // Right 853 {1, 4, 7, 2, 5, 3, 6, 1}, // Down 854 {7, 6, 5, 4, 3, 2, 1, 7}, // Left 855 {6, 3, 5, 2, 7, 4, 1, 6} // Up 856 }; 857 858 for (size_t key_index = 0; key_index < arraysize(arrow_keys); key_index++) { 859 ToggleOverview(); 860 for (size_t i = 0; i < test_windows + 1; i++) { 861 SendKey(arrow_keys[key_index]); 862 // TODO(nsatragno): Add a more readable error message by constructing a 863 // string from the window IDs. 864 EXPECT_EQ(GetSelectedWindow()->id(), 865 index_path_for_direction[key_index][i]); 866 } 867 ToggleOverview(); 868 } 869 } 870 871 // Tests basic selection across multiple monitors. 872 TEST_F(WindowSelectorTest, BasicMultiMonitorArrowKeyNavigation) { 873 if (!SupportsMultipleDisplays()) 874 return; 875 876 UpdateDisplay("400x400,400x400"); 877 gfx::Rect bounds1(0, 0, 100, 100); 878 gfx::Rect bounds2(450, 0, 100, 100); 879 scoped_ptr<aura::Window> window4(CreateWindow(bounds2)); 880 scoped_ptr<aura::Window> window3(CreateWindow(bounds2)); 881 scoped_ptr<aura::Window> window2(CreateWindow(bounds1)); 882 scoped_ptr<aura::Window> window1(CreateWindow(bounds1)); 883 884 885 ToggleOverview(); 886 887 SendKey(ui::VKEY_RIGHT); 888 EXPECT_EQ(GetSelectedWindow(), window1.get()); 889 SendKey(ui::VKEY_RIGHT); 890 EXPECT_EQ(GetSelectedWindow(), window2.get()); 891 SendKey(ui::VKEY_RIGHT); 892 EXPECT_EQ(GetSelectedWindow(), window3.get()); 893 SendKey(ui::VKEY_RIGHT); 894 EXPECT_EQ(GetSelectedWindow(), window4.get()); 895 } 896 897 // Tests selecting a window in overview mode with the return key. 898 TEST_F(WindowSelectorTest, SelectWindowWithReturnKey) { 899 gfx::Rect bounds(0, 0, 100, 100); 900 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 901 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 902 ToggleOverview(); 903 904 // Pressing the return key without a selection widget should not do anything. 905 SendKey(ui::VKEY_RETURN); 906 EXPECT_TRUE(IsSelecting()); 907 908 // Select the first window. 909 SendKey(ui::VKEY_RIGHT); 910 SendKey(ui::VKEY_RETURN); 911 ASSERT_FALSE(IsSelecting()); 912 EXPECT_TRUE(wm::IsActiveWindow(window1.get())); 913 914 // Select the second window. 915 ToggleOverview(); 916 SendKey(ui::VKEY_RIGHT); 917 SendKey(ui::VKEY_RIGHT); 918 SendKey(ui::VKEY_RETURN); 919 EXPECT_FALSE(IsSelecting()); 920 EXPECT_TRUE(wm::IsActiveWindow(window2.get())); 921 } 922 923 // Tests that overview mode hides the callout widget. 924 TEST_F(WindowSelectorTest, WindowOverviewHidesCalloutWidgets) { 925 scoped_ptr<aura::Window> panel1(CreatePanelWindow(gfx::Rect(0, 0, 100, 100))); 926 scoped_ptr<aura::Window> panel2(CreatePanelWindow(gfx::Rect(0, 0, 100, 100))); 927 PanelLayoutManager* panel_manager = 928 static_cast<PanelLayoutManager*>(panel1->parent()->layout_manager()); 929 930 // By default, panel callout widgets are visible. 931 EXPECT_TRUE( 932 panel_manager->GetCalloutWidgetForPanel(panel1.get())->IsVisible()); 933 EXPECT_TRUE( 934 panel_manager->GetCalloutWidgetForPanel(panel2.get())->IsVisible()); 935 936 // Toggling the overview should hide the callout widgets. 937 ToggleOverview(); 938 EXPECT_FALSE( 939 panel_manager->GetCalloutWidgetForPanel(panel1.get())->IsVisible()); 940 EXPECT_FALSE( 941 panel_manager->GetCalloutWidgetForPanel(panel2.get())->IsVisible()); 942 943 // Ending the overview should show them again. 944 ToggleOverview(); 945 EXPECT_TRUE( 946 panel_manager->GetCalloutWidgetForPanel(panel1.get())->IsVisible()); 947 EXPECT_TRUE( 948 panel_manager->GetCalloutWidgetForPanel(panel2.get())->IsVisible()); 949 } 950 951 // Tests that when panels are grouped that the close button only closes the 952 // currently active panel. After the removal window selection should still be 953 // active, and the label should have changed. Removing the last panel should 954 // cause selection to end. 955 TEST_F(WindowSelectorTest, CloseButtonOnPanels) { 956 scoped_ptr<views::Widget> widget1(CreatePanelWindowWidget( 957 gfx::Rect(0, 0, 300, 100))); 958 scoped_ptr<views::Widget> widget2(CreatePanelWindowWidget( 959 gfx::Rect(100, 0, 100, 100))); 960 aura::Window* window1 = widget1->GetNativeWindow(); 961 aura::Window* window2 = widget2->GetNativeWindow(); 962 base::string16 panel1_title = base::UTF8ToUTF16("Panel 1"); 963 base::string16 panel2_title = base::UTF8ToUTF16("Panel 2"); 964 window1->set_title(panel1_title); 965 window2->set_title(panel2_title); 966 wm::ActivateWindow(window1); 967 ToggleOverview(); 968 969 gfx::RectF bounds1 = GetTransformedBoundsInRootWindow(window1); 970 gfx::Point point1(bounds1.top_right().x() - 1, bounds1.top_right().y() - 1); 971 aura::test::EventGenerator event_generator1(window1->GetRootWindow(), point1); 972 973 EXPECT_FALSE(widget1->IsClosed()); 974 event_generator1.ClickLeftButton(); 975 EXPECT_TRUE(widget1->IsClosed()); 976 RunAllPendingInMessageLoop(); 977 EXPECT_TRUE(IsSelecting()); 978 WindowSelectorItem* window_item = GetWindowItemsForRoot(0).front(); 979 EXPECT_FALSE(window_item->empty()); 980 EXPECT_TRUE(window_item->Contains(window2)); 981 EXPECT_TRUE(GetCloseButton(window_item)->IsVisible()); 982 983 views::Widget* widget = GetLabelWidget(window_item); 984 views::Label* label = static_cast<views::Label*>(widget->GetContentsView()); 985 EXPECT_EQ(label->text(), panel2_title); 986 987 gfx::RectF bounds2 = GetTransformedBoundsInRootWindow(window2); 988 gfx::Point point2(bounds2.top_right().x() - 1, bounds2.top_right().y() - 1); 989 aura::test::EventGenerator event_generator2(window2->GetRootWindow(), point2); 990 991 EXPECT_FALSE(widget2->IsClosed()); 992 event_generator2.ClickLeftButton(); 993 EXPECT_TRUE(widget2->IsClosed()); 994 RunAllPendingInMessageLoop(); 995 EXPECT_FALSE(IsSelecting()); 996 } 997 998 } // namespace ash 999