1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ash/root_window_controller.h" 6 7 #include "ash/session/session_state_delegate.h" 8 #include "ash/shelf/shelf_layout_manager.h" 9 #include "ash/shell.h" 10 #include "ash/shell_window_ids.h" 11 #include "ash/system/tray/system_tray_delegate.h" 12 #include "ash/test/ash_test_base.h" 13 #include "ash/wm/system_modal_container_layout_manager.h" 14 #include "ash/wm/window_properties.h" 15 #include "ash/wm/window_state.h" 16 #include "ash/wm/window_util.h" 17 #include "base/command_line.h" 18 #include "ui/aura/client/focus_change_observer.h" 19 #include "ui/aura/client/focus_client.h" 20 #include "ui/aura/client/window_tree_client.h" 21 #include "ui/aura/env.h" 22 #include "ui/aura/test/event_generator.h" 23 #include "ui/aura/test/test_window_delegate.h" 24 #include "ui/aura/test/test_windows.h" 25 #include "ui/aura/window.h" 26 #include "ui/aura/window_event_dispatcher.h" 27 #include "ui/aura/window_tracker.h" 28 #include "ui/base/ime/dummy_text_input_client.h" 29 #include "ui/base/ime/input_method.h" 30 #include "ui/base/ime/text_input_client.h" 31 #include "ui/base/ime/text_input_focus_manager.h" 32 #include "ui/base/ui_base_switches_util.h" 33 #include "ui/events/test/test_event_handler.h" 34 #include "ui/keyboard/keyboard_controller_proxy.h" 35 #include "ui/keyboard/keyboard_switches.h" 36 #include "ui/keyboard/keyboard_util.h" 37 #include "ui/views/controls/menu/menu_controller.h" 38 #include "ui/views/widget/widget.h" 39 #include "ui/views/widget/widget_delegate.h" 40 41 using aura::Window; 42 using views::Widget; 43 44 namespace ash { 45 namespace { 46 47 class TestDelegate : public views::WidgetDelegateView { 48 public: 49 explicit TestDelegate(bool system_modal) : system_modal_(system_modal) {} 50 virtual ~TestDelegate() {} 51 52 // Overridden from views::WidgetDelegate: 53 virtual views::View* GetContentsView() OVERRIDE { 54 return this; 55 } 56 57 virtual ui::ModalType GetModalType() const OVERRIDE { 58 return system_modal_ ? ui::MODAL_TYPE_SYSTEM : ui::MODAL_TYPE_NONE; 59 } 60 61 private: 62 bool system_modal_; 63 64 DISALLOW_COPY_AND_ASSIGN(TestDelegate); 65 }; 66 67 class DeleteOnBlurDelegate : public aura::test::TestWindowDelegate, 68 public aura::client::FocusChangeObserver { 69 public: 70 DeleteOnBlurDelegate() : window_(NULL) {} 71 virtual ~DeleteOnBlurDelegate() {} 72 73 void SetWindow(aura::Window* window) { 74 window_ = window; 75 aura::client::SetFocusChangeObserver(window_, this); 76 } 77 78 private: 79 // aura::test::TestWindowDelegate overrides: 80 virtual bool CanFocus() OVERRIDE { 81 return true; 82 } 83 84 // aura::client::FocusChangeObserver implementation: 85 virtual void OnWindowFocused(aura::Window* gained_focus, 86 aura::Window* lost_focus) OVERRIDE { 87 if (window_ == lost_focus) 88 delete window_; 89 } 90 91 aura::Window* window_; 92 93 DISALLOW_COPY_AND_ASSIGN(DeleteOnBlurDelegate); 94 }; 95 96 } // namespace 97 98 namespace test { 99 100 class RootWindowControllerTest : public test::AshTestBase { 101 public: 102 views::Widget* CreateTestWidget(const gfx::Rect& bounds) { 103 views::Widget* widget = views::Widget::CreateWindowWithContextAndBounds( 104 NULL, CurrentContext(), bounds); 105 widget->Show(); 106 return widget; 107 } 108 109 views::Widget* CreateModalWidget(const gfx::Rect& bounds) { 110 views::Widget* widget = views::Widget::CreateWindowWithContextAndBounds( 111 new TestDelegate(true), CurrentContext(), bounds); 112 widget->Show(); 113 return widget; 114 } 115 116 views::Widget* CreateModalWidgetWithParent(const gfx::Rect& bounds, 117 gfx::NativeWindow parent) { 118 views::Widget* widget = 119 views::Widget::CreateWindowWithParentAndBounds(new TestDelegate(true), 120 parent, 121 bounds); 122 widget->Show(); 123 return widget; 124 } 125 126 aura::Window* GetModalContainer(aura::Window* root_window) { 127 return Shell::GetContainer(root_window, 128 ash::kShellWindowId_SystemModalContainer); 129 } 130 }; 131 132 TEST_F(RootWindowControllerTest, MoveWindows_Basic) { 133 if (!SupportsMultipleDisplays()) 134 return; 135 // Windows origin should be doubled when moved to the 1st display. 136 UpdateDisplay("600x600,300x300"); 137 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 138 RootWindowController* controller = Shell::GetPrimaryRootWindowController(); 139 ShelfLayoutManager* shelf_layout_manager = 140 controller->GetShelfLayoutManager(); 141 shelf_layout_manager->SetAutoHideBehavior( 142 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); 143 144 views::Widget* normal = CreateTestWidget(gfx::Rect(650, 10, 100, 100)); 145 EXPECT_EQ(root_windows[1], normal->GetNativeView()->GetRootWindow()); 146 EXPECT_EQ("650,10 100x100", normal->GetWindowBoundsInScreen().ToString()); 147 EXPECT_EQ("50,10 100x100", 148 normal->GetNativeView()->GetBoundsInRootWindow().ToString()); 149 150 views::Widget* maximized = CreateTestWidget(gfx::Rect(700, 10, 100, 100)); 151 maximized->Maximize(); 152 EXPECT_EQ(root_windows[1], maximized->GetNativeView()->GetRootWindow()); 153 EXPECT_EQ("600,0 300x253", maximized->GetWindowBoundsInScreen().ToString()); 154 EXPECT_EQ("0,0 300x253", 155 maximized->GetNativeView()->GetBoundsInRootWindow().ToString()); 156 157 views::Widget* minimized = CreateTestWidget(gfx::Rect(800, 10, 100, 100)); 158 minimized->Minimize(); 159 EXPECT_EQ(root_windows[1], minimized->GetNativeView()->GetRootWindow()); 160 EXPECT_EQ("800,10 100x100", 161 minimized->GetWindowBoundsInScreen().ToString()); 162 163 views::Widget* fullscreen = CreateTestWidget(gfx::Rect(850, 10, 100, 100)); 164 fullscreen->SetFullscreen(true); 165 EXPECT_EQ(root_windows[1], fullscreen->GetNativeView()->GetRootWindow()); 166 167 EXPECT_EQ("600,0 300x300", 168 fullscreen->GetWindowBoundsInScreen().ToString()); 169 EXPECT_EQ("0,0 300x300", 170 fullscreen->GetNativeView()->GetBoundsInRootWindow().ToString()); 171 172 views::Widget* unparented_control = new Widget; 173 Widget::InitParams params; 174 params.bounds = gfx::Rect(650, 10, 100, 100); 175 params.context = CurrentContext(); 176 params.type = Widget::InitParams::TYPE_CONTROL; 177 unparented_control->Init(params); 178 EXPECT_EQ(root_windows[1], 179 unparented_control->GetNativeView()->GetRootWindow()); 180 EXPECT_EQ(kShellWindowId_UnparentedControlContainer, 181 unparented_control->GetNativeView()->parent()->id()); 182 183 aura::Window* panel = CreateTestWindowInShellWithDelegateAndType( 184 NULL, ui::wm::WINDOW_TYPE_PANEL, 0, gfx::Rect(700, 100, 100, 100)); 185 EXPECT_EQ(root_windows[1], panel->GetRootWindow()); 186 EXPECT_EQ(kShellWindowId_PanelContainer, panel->parent()->id()); 187 188 // Make sure a window that will delete itself when losing focus 189 // will not crash. 190 aura::WindowTracker tracker; 191 DeleteOnBlurDelegate delete_on_blur_delegate; 192 aura::Window* d2 = CreateTestWindowInShellWithDelegate( 193 &delete_on_blur_delegate, 0, gfx::Rect(50, 50, 100, 100)); 194 delete_on_blur_delegate.SetWindow(d2); 195 aura::client::GetFocusClient(root_windows[0])->FocusWindow(d2); 196 tracker.Add(d2); 197 198 UpdateDisplay("600x600"); 199 200 // d2 must have been deleted. 201 EXPECT_FALSE(tracker.Contains(d2)); 202 203 EXPECT_EQ(root_windows[0], normal->GetNativeView()->GetRootWindow()); 204 EXPECT_EQ("100,20 100x100", normal->GetWindowBoundsInScreen().ToString()); 205 EXPECT_EQ("100,20 100x100", 206 normal->GetNativeView()->GetBoundsInRootWindow().ToString()); 207 208 // Maximized area on primary display has 3px (given as 209 // kAutoHideSize in shelf_layout_manager.cc) inset at the bottom. 210 211 // First clear fullscreen status, since both fullscreen and maximized windows 212 // share the same desktop workspace, which cancels the shelf status. 213 fullscreen->SetFullscreen(false); 214 EXPECT_EQ(root_windows[0], maximized->GetNativeView()->GetRootWindow()); 215 EXPECT_EQ("0,0 600x597", 216 maximized->GetWindowBoundsInScreen().ToString()); 217 EXPECT_EQ("0,0 600x597", 218 maximized->GetNativeView()->GetBoundsInRootWindow().ToString()); 219 220 // Set fullscreen to true. In that case the 3px inset becomes invisible so 221 // the maximized window can also use the area fully. 222 fullscreen->SetFullscreen(true); 223 EXPECT_EQ(root_windows[0], maximized->GetNativeView()->GetRootWindow()); 224 EXPECT_EQ("0,0 600x600", 225 maximized->GetWindowBoundsInScreen().ToString()); 226 EXPECT_EQ("0,0 600x600", 227 maximized->GetNativeView()->GetBoundsInRootWindow().ToString()); 228 229 EXPECT_EQ(root_windows[0], minimized->GetNativeView()->GetRootWindow()); 230 EXPECT_EQ("400,20 100x100", 231 minimized->GetWindowBoundsInScreen().ToString()); 232 233 EXPECT_EQ(root_windows[0], fullscreen->GetNativeView()->GetRootWindow()); 234 EXPECT_TRUE(fullscreen->IsFullscreen()); 235 EXPECT_EQ("0,0 600x600", 236 fullscreen->GetWindowBoundsInScreen().ToString()); 237 EXPECT_EQ("0,0 600x600", 238 fullscreen->GetNativeView()->GetBoundsInRootWindow().ToString()); 239 240 // Test if the restore bounds are correctly updated. 241 wm::GetWindowState(maximized->GetNativeView())->Restore(); 242 EXPECT_EQ("200,20 100x100", maximized->GetWindowBoundsInScreen().ToString()); 243 EXPECT_EQ("200,20 100x100", 244 maximized->GetNativeView()->GetBoundsInRootWindow().ToString()); 245 246 fullscreen->SetFullscreen(false); 247 EXPECT_EQ("500,20 100x100", 248 fullscreen->GetWindowBoundsInScreen().ToString()); 249 EXPECT_EQ("500,20 100x100", 250 fullscreen->GetNativeView()->GetBoundsInRootWindow().ToString()); 251 252 // Test if the unparented widget has moved. 253 EXPECT_EQ(root_windows[0], 254 unparented_control->GetNativeView()->GetRootWindow()); 255 EXPECT_EQ(kShellWindowId_UnparentedControlContainer, 256 unparented_control->GetNativeView()->parent()->id()); 257 258 // Test if the panel has moved. 259 EXPECT_EQ(root_windows[0], panel->GetRootWindow()); 260 EXPECT_EQ(kShellWindowId_PanelContainer, panel->parent()->id()); 261 } 262 263 TEST_F(RootWindowControllerTest, MoveWindows_Modal) { 264 if (!SupportsMultipleDisplays()) 265 return; 266 267 UpdateDisplay("500x500,500x500"); 268 269 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 270 // Emulate virtual screen coordinate system. 271 root_windows[0]->SetBounds(gfx::Rect(0, 0, 500, 500)); 272 root_windows[1]->SetBounds(gfx::Rect(500, 0, 500, 500)); 273 274 views::Widget* normal = CreateTestWidget(gfx::Rect(300, 10, 100, 100)); 275 EXPECT_EQ(root_windows[0], normal->GetNativeView()->GetRootWindow()); 276 EXPECT_TRUE(wm::IsActiveWindow(normal->GetNativeView())); 277 278 views::Widget* modal = CreateModalWidget(gfx::Rect(650, 10, 100, 100)); 279 EXPECT_EQ(root_windows[1], modal->GetNativeView()->GetRootWindow()); 280 EXPECT_TRUE(GetModalContainer(root_windows[1])->Contains( 281 modal->GetNativeView())); 282 EXPECT_TRUE(wm::IsActiveWindow(modal->GetNativeView())); 283 284 aura::test::EventGenerator generator_1st(root_windows[0]); 285 generator_1st.ClickLeftButton(); 286 EXPECT_TRUE(wm::IsActiveWindow(modal->GetNativeView())); 287 288 UpdateDisplay("500x500"); 289 EXPECT_EQ(root_windows[0], modal->GetNativeView()->GetRootWindow()); 290 EXPECT_TRUE(wm::IsActiveWindow(modal->GetNativeView())); 291 generator_1st.ClickLeftButton(); 292 EXPECT_TRUE(wm::IsActiveWindow(modal->GetNativeView())); 293 } 294 295 TEST_F(RootWindowControllerTest, ModalContainer) { 296 UpdateDisplay("600x600"); 297 Shell* shell = Shell::GetInstance(); 298 RootWindowController* controller = shell->GetPrimaryRootWindowController(); 299 EXPECT_EQ(user::LOGGED_IN_USER, 300 shell->system_tray_delegate()->GetUserLoginStatus()); 301 EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer) 302 ->layout_manager(), 303 controller->GetSystemModalLayoutManager(NULL)); 304 305 views::Widget* session_modal_widget = 306 CreateModalWidget(gfx::Rect(300, 10, 100, 100)); 307 EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer) 308 ->layout_manager(), 309 controller->GetSystemModalLayoutManager( 310 session_modal_widget->GetNativeView())); 311 312 shell->session_state_delegate()->LockScreen(); 313 EXPECT_EQ(user::LOGGED_IN_LOCKED, 314 shell->system_tray_delegate()->GetUserLoginStatus()); 315 EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer) 316 ->layout_manager(), 317 controller->GetSystemModalLayoutManager(NULL)); 318 319 aura::Window* lock_container = 320 controller->GetContainer(kShellWindowId_LockScreenContainer); 321 views::Widget* lock_modal_widget = 322 CreateModalWidgetWithParent(gfx::Rect(300, 10, 100, 100), lock_container); 323 EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer) 324 ->layout_manager(), 325 controller->GetSystemModalLayoutManager( 326 lock_modal_widget->GetNativeView())); 327 EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer) 328 ->layout_manager(), 329 controller->GetSystemModalLayoutManager( 330 session_modal_widget->GetNativeView())); 331 332 shell->session_state_delegate()->UnlockScreen(); 333 } 334 335 TEST_F(RootWindowControllerTest, ModalContainerNotLoggedInLoggedIn) { 336 UpdateDisplay("600x600"); 337 Shell* shell = Shell::GetInstance(); 338 339 // Configure login screen environment. 340 SetUserLoggedIn(false); 341 EXPECT_EQ(user::LOGGED_IN_NONE, 342 shell->system_tray_delegate()->GetUserLoginStatus()); 343 EXPECT_EQ(0, shell->session_state_delegate()->NumberOfLoggedInUsers()); 344 EXPECT_FALSE(shell->session_state_delegate()->IsActiveUserSessionStarted()); 345 346 RootWindowController* controller = shell->GetPrimaryRootWindowController(); 347 EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer) 348 ->layout_manager(), 349 controller->GetSystemModalLayoutManager(NULL)); 350 351 aura::Window* lock_container = 352 controller->GetContainer(kShellWindowId_LockScreenContainer); 353 views::Widget* login_modal_widget = 354 CreateModalWidgetWithParent(gfx::Rect(300, 10, 100, 100), lock_container); 355 EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer) 356 ->layout_manager(), 357 controller->GetSystemModalLayoutManager( 358 login_modal_widget->GetNativeView())); 359 login_modal_widget->Close(); 360 361 // Configure user session environment. 362 SetUserLoggedIn(true); 363 SetSessionStarted(true); 364 EXPECT_EQ(user::LOGGED_IN_USER, 365 shell->system_tray_delegate()->GetUserLoginStatus()); 366 EXPECT_EQ(1, shell->session_state_delegate()->NumberOfLoggedInUsers()); 367 EXPECT_TRUE(shell->session_state_delegate()->IsActiveUserSessionStarted()); 368 EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer) 369 ->layout_manager(), 370 controller->GetSystemModalLayoutManager(NULL)); 371 372 views::Widget* session_modal_widget = 373 CreateModalWidget(gfx::Rect(300, 10, 100, 100)); 374 EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer) 375 ->layout_manager(), 376 controller->GetSystemModalLayoutManager( 377 session_modal_widget->GetNativeView())); 378 } 379 380 TEST_F(RootWindowControllerTest, ModalContainerBlockedSession) { 381 UpdateDisplay("600x600"); 382 Shell* shell = Shell::GetInstance(); 383 RootWindowController* controller = shell->GetPrimaryRootWindowController(); 384 aura::Window* lock_container = 385 controller->GetContainer(kShellWindowId_LockScreenContainer); 386 for (int block_reason = FIRST_BLOCK_REASON; 387 block_reason < NUMBER_OF_BLOCK_REASONS; 388 ++block_reason) { 389 views::Widget* session_modal_widget = 390 CreateModalWidget(gfx::Rect(300, 10, 100, 100)); 391 EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer) 392 ->layout_manager(), 393 controller->GetSystemModalLayoutManager( 394 session_modal_widget->GetNativeView())); 395 EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer) 396 ->layout_manager(), 397 controller->GetSystemModalLayoutManager(NULL)); 398 session_modal_widget->Close(); 399 400 BlockUserSession(static_cast<UserSessionBlockReason>(block_reason)); 401 402 EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer) 403 ->layout_manager(), 404 controller->GetSystemModalLayoutManager(NULL)); 405 406 views::Widget* lock_modal_widget = 407 CreateModalWidgetWithParent(gfx::Rect(300, 10, 100, 100), 408 lock_container); 409 EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer) 410 ->layout_manager(), 411 controller->GetSystemModalLayoutManager( 412 lock_modal_widget->GetNativeView())); 413 414 session_modal_widget = 415 CreateModalWidget(gfx::Rect(300, 10, 100, 100)); 416 EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer) 417 ->layout_manager(), 418 controller->GetSystemModalLayoutManager( 419 session_modal_widget->GetNativeView())); 420 session_modal_widget->Close(); 421 422 lock_modal_widget->Close(); 423 UnblockUserSession(); 424 } 425 } 426 427 TEST_F(RootWindowControllerTest, GetWindowForFullscreenMode) { 428 UpdateDisplay("600x600"); 429 RootWindowController* controller = 430 Shell::GetInstance()->GetPrimaryRootWindowController(); 431 432 Widget* w1 = CreateTestWidget(gfx::Rect(0, 0, 100, 100)); 433 w1->Maximize(); 434 Widget* w2 = CreateTestWidget(gfx::Rect(0, 0, 100, 100)); 435 w2->SetFullscreen(true); 436 // |w3| is a transient child of |w2|. 437 Widget* w3 = Widget::CreateWindowWithParentAndBounds(NULL, 438 w2->GetNativeWindow(), gfx::Rect(0, 0, 100, 100)); 439 440 // Test that GetWindowForFullscreenMode() finds the fullscreen window when one 441 // of its transient children is active. 442 w3->Activate(); 443 EXPECT_EQ(w2->GetNativeWindow(), controller->GetWindowForFullscreenMode()); 444 445 // If the topmost window is not fullscreen, it returns NULL. 446 w1->Activate(); 447 EXPECT_EQ(NULL, controller->GetWindowForFullscreenMode()); 448 w1->Close(); 449 w3->Close(); 450 451 // Only w2 remains, if minimized GetWindowForFullscreenMode should return 452 // NULL. 453 w2->Activate(); 454 EXPECT_EQ(w2->GetNativeWindow(), controller->GetWindowForFullscreenMode()); 455 w2->Minimize(); 456 EXPECT_EQ(NULL, controller->GetWindowForFullscreenMode()); 457 } 458 459 TEST_F(RootWindowControllerTest, MultipleDisplaysGetWindowForFullscreenMode) { 460 if (!SupportsMultipleDisplays()) 461 return; 462 463 UpdateDisplay("600x600,600x600"); 464 Shell::RootWindowControllerList controllers = 465 Shell::GetInstance()->GetAllRootWindowControllers(); 466 467 Widget* w1 = CreateTestWidget(gfx::Rect(0, 0, 100, 100)); 468 w1->Maximize(); 469 Widget* w2 = CreateTestWidget(gfx::Rect(0, 0, 100, 100)); 470 w2->SetFullscreen(true); 471 Widget* w3 = CreateTestWidget(gfx::Rect(600, 0, 100, 100)); 472 473 EXPECT_EQ(w1->GetNativeWindow()->GetRootWindow(), 474 controllers[0]->GetRootWindow()); 475 EXPECT_EQ(w2->GetNativeWindow()->GetRootWindow(), 476 controllers[0]->GetRootWindow()); 477 EXPECT_EQ(w3->GetNativeWindow()->GetRootWindow(), 478 controllers[1]->GetRootWindow()); 479 480 w1->Activate(); 481 EXPECT_EQ(NULL, controllers[0]->GetWindowForFullscreenMode()); 482 EXPECT_EQ(NULL, controllers[1]->GetWindowForFullscreenMode()); 483 484 w2->Activate(); 485 EXPECT_EQ(w2->GetNativeWindow(), 486 controllers[0]->GetWindowForFullscreenMode()); 487 EXPECT_EQ(NULL, controllers[1]->GetWindowForFullscreenMode()); 488 489 // Verify that the first root window controller remains in fullscreen mode 490 // when a window on the other display is activated. 491 w3->Activate(); 492 EXPECT_EQ(w2->GetNativeWindow(), 493 controllers[0]->GetWindowForFullscreenMode()); 494 EXPECT_EQ(NULL, controllers[1]->GetWindowForFullscreenMode()); 495 } 496 497 // Test that user session window can't be focused if user session blocked by 498 // some overlapping UI. 499 TEST_F(RootWindowControllerTest, FocusBlockedWindow) { 500 UpdateDisplay("600x600"); 501 RootWindowController* controller = 502 Shell::GetInstance()->GetPrimaryRootWindowController(); 503 aura::Window* lock_container = 504 controller->GetContainer(kShellWindowId_LockScreenContainer); 505 aura::Window* lock_window = Widget::CreateWindowWithParentAndBounds(NULL, 506 lock_container, gfx::Rect(0, 0, 100, 100))->GetNativeView(); 507 lock_window->Show(); 508 aura::Window* session_window = 509 CreateTestWidget(gfx::Rect(0, 0, 100, 100))->GetNativeView(); 510 session_window->Show(); 511 512 for (int block_reason = FIRST_BLOCK_REASON; 513 block_reason < NUMBER_OF_BLOCK_REASONS; 514 ++block_reason) { 515 BlockUserSession(static_cast<UserSessionBlockReason>(block_reason)); 516 lock_window->Focus(); 517 EXPECT_TRUE(lock_window->HasFocus()); 518 session_window->Focus(); 519 EXPECT_FALSE(session_window->HasFocus()); 520 UnblockUserSession(); 521 } 522 } 523 524 // Tracks whether OnWindowDestroying() has been invoked. 525 class DestroyedWindowObserver : public aura::WindowObserver { 526 public: 527 DestroyedWindowObserver() : destroyed_(false), window_(NULL) {} 528 virtual ~DestroyedWindowObserver() { 529 Shutdown(); 530 } 531 532 void SetWindow(Window* window) { 533 window_ = window; 534 window->AddObserver(this); 535 } 536 537 bool destroyed() const { return destroyed_; } 538 539 // WindowObserver overrides: 540 virtual void OnWindowDestroying(Window* window) OVERRIDE { 541 destroyed_ = true; 542 Shutdown(); 543 } 544 545 private: 546 void Shutdown() { 547 if (!window_) 548 return; 549 window_->RemoveObserver(this); 550 window_ = NULL; 551 } 552 553 bool destroyed_; 554 Window* window_; 555 556 DISALLOW_COPY_AND_ASSIGN(DestroyedWindowObserver); 557 }; 558 559 // Verifies shutdown doesn't delete windows that are not owned by the parent. 560 TEST_F(RootWindowControllerTest, DontDeleteWindowsNotOwnedByParent) { 561 DestroyedWindowObserver observer1; 562 aura::test::TestWindowDelegate delegate1; 563 aura::Window* window1 = new aura::Window(&delegate1); 564 window1->SetType(ui::wm::WINDOW_TYPE_CONTROL); 565 window1->set_owned_by_parent(false); 566 observer1.SetWindow(window1); 567 window1->Init(aura::WINDOW_LAYER_NOT_DRAWN); 568 aura::client::ParentWindowWithContext( 569 window1, Shell::GetInstance()->GetPrimaryRootWindow(), gfx::Rect()); 570 571 DestroyedWindowObserver observer2; 572 aura::Window* window2 = new aura::Window(NULL); 573 window2->set_owned_by_parent(false); 574 observer2.SetWindow(window2); 575 window2->Init(aura::WINDOW_LAYER_NOT_DRAWN); 576 Shell::GetInstance()->GetPrimaryRootWindow()->AddChild(window2); 577 578 Shell::GetInstance()->GetPrimaryRootWindowController()->CloseChildWindows(); 579 580 ASSERT_FALSE(observer1.destroyed()); 581 delete window1; 582 583 ASSERT_FALSE(observer2.destroyed()); 584 delete window2; 585 } 586 587 typedef test::NoSessionAshTestBase NoSessionRootWindowControllerTest; 588 589 // Make sure that an event handler exists for entire display area. 590 TEST_F(NoSessionRootWindowControllerTest, Event) { 591 // Hide the shelf since it might otherwise get an event target. 592 RootWindowController* controller = Shell::GetPrimaryRootWindowController(); 593 ShelfLayoutManager* shelf_layout_manager = 594 controller->GetShelfLayoutManager(); 595 shelf_layout_manager->SetAutoHideBehavior( 596 ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN); 597 598 aura::Window* root = Shell::GetPrimaryRootWindow(); 599 const gfx::Size size = root->bounds().size(); 600 aura::Window* event_target = root->GetEventHandlerForPoint(gfx::Point(0, 0)); 601 EXPECT_TRUE(event_target); 602 EXPECT_EQ(event_target, 603 root->GetEventHandlerForPoint(gfx::Point(0, size.height() - 1))); 604 EXPECT_EQ(event_target, 605 root->GetEventHandlerForPoint(gfx::Point(size.width() - 1, 0))); 606 EXPECT_EQ(event_target, 607 root->GetEventHandlerForPoint(gfx::Point(0, size.height() - 1))); 608 EXPECT_EQ(event_target, 609 root->GetEventHandlerForPoint( 610 gfx::Point(size.width() - 1, size.height() - 1))); 611 } 612 613 class VirtualKeyboardRootWindowControllerTest : public RootWindowControllerTest 614 { 615 public: 616 VirtualKeyboardRootWindowControllerTest() {}; 617 virtual ~VirtualKeyboardRootWindowControllerTest() {}; 618 619 virtual void SetUp() OVERRIDE { 620 CommandLine::ForCurrentProcess()->AppendSwitch( 621 keyboard::switches::kEnableVirtualKeyboard); 622 test::AshTestBase::SetUp(); 623 Shell::GetPrimaryRootWindowController()->ActivateKeyboard( 624 keyboard::KeyboardController::GetInstance()); 625 } 626 627 private: 628 DISALLOW_COPY_AND_ASSIGN(VirtualKeyboardRootWindowControllerTest); 629 }; 630 631 class MockTextInputClient : public ui::DummyTextInputClient { 632 public: 633 MockTextInputClient() : 634 ui::DummyTextInputClient(ui::TEXT_INPUT_TYPE_TEXT) {} 635 636 virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE { 637 visible_rect_ = rect; 638 } 639 640 const gfx::Rect& visible_rect() const { 641 return visible_rect_; 642 } 643 644 private: 645 gfx::Rect visible_rect_; 646 647 DISALLOW_COPY_AND_ASSIGN(MockTextInputClient); 648 }; 649 650 // Test for http://crbug.com/297858. Virtual keyboard container should only show 651 // on primary root window. 652 TEST_F(VirtualKeyboardRootWindowControllerTest, 653 VirtualKeyboardOnPrimaryRootWindowOnly) { 654 if (!SupportsMultipleDisplays()) 655 return; 656 657 UpdateDisplay("500x500,500x500"); 658 659 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 660 aura::Window* primary_root_window = Shell::GetPrimaryRootWindow(); 661 aura::Window* secondary_root_window = 662 root_windows[0] == primary_root_window ? 663 root_windows[1] : root_windows[0]; 664 665 ASSERT_TRUE(Shell::GetContainer(primary_root_window, 666 kShellWindowId_VirtualKeyboardContainer)); 667 ASSERT_FALSE(Shell::GetContainer(secondary_root_window, 668 kShellWindowId_VirtualKeyboardContainer)); 669 } 670 671 // Test for http://crbug.com/263599. Virtual keyboard should be able to receive 672 // events at blocked user session. 673 TEST_F(VirtualKeyboardRootWindowControllerTest, 674 ClickVirtualKeyboardInBlockedWindow) { 675 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 676 aura::Window* keyboard_container = 677 Shell::GetContainer(root_window, kShellWindowId_VirtualKeyboardContainer); 678 ASSERT_TRUE(keyboard_container); 679 keyboard_container->Show(); 680 681 aura::Window* keyboard_window = keyboard::KeyboardController::GetInstance()-> 682 proxy()->GetKeyboardWindow(); 683 keyboard_container->AddChild(keyboard_window); 684 keyboard_window->set_owned_by_parent(false); 685 keyboard_window->SetBounds(gfx::Rect()); 686 keyboard_window->Show(); 687 688 ui::test::TestEventHandler handler; 689 root_window->AddPreTargetHandler(&handler); 690 691 aura::test::EventGenerator event_generator(root_window, keyboard_window); 692 event_generator.ClickLeftButton(); 693 int expected_mouse_presses = 1; 694 EXPECT_EQ(expected_mouse_presses, handler.num_mouse_events() / 2); 695 696 for (int block_reason = FIRST_BLOCK_REASON; 697 block_reason < NUMBER_OF_BLOCK_REASONS; 698 ++block_reason) { 699 BlockUserSession(static_cast<UserSessionBlockReason>(block_reason)); 700 event_generator.ClickLeftButton(); 701 expected_mouse_presses++; 702 EXPECT_EQ(expected_mouse_presses, handler.num_mouse_events() / 2); 703 UnblockUserSession(); 704 } 705 root_window->RemovePreTargetHandler(&handler); 706 } 707 708 // Test for http://crbug.com/299787. RootWindowController should delete 709 // the old container since the keyboard controller creates a new window in 710 // GetWindowContainer(). 711 TEST_F(VirtualKeyboardRootWindowControllerTest, 712 DeleteOldContainerOnVirtualKeyboardInit) { 713 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 714 aura::Window* keyboard_container = 715 Shell::GetContainer(root_window, kShellWindowId_VirtualKeyboardContainer); 716 ASSERT_TRUE(keyboard_container); 717 // Track the keyboard container window. 718 aura::WindowTracker tracker; 719 tracker.Add(keyboard_container); 720 // Mock a login user profile change to reinitialize the keyboard. 721 ash::Shell::GetInstance()->OnLoginUserProfilePrepared(); 722 // keyboard_container should no longer be present. 723 EXPECT_FALSE(tracker.Contains(keyboard_container)); 724 } 725 726 // Test for crbug.com/342524. After user login, the work space should restore to 727 // full screen. 728 TEST_F(VirtualKeyboardRootWindowControllerTest, RestoreWorkspaceAfterLogin) { 729 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 730 aura::Window* keyboard_container = 731 Shell::GetContainer(root_window, kShellWindowId_VirtualKeyboardContainer); 732 keyboard_container->Show(); 733 keyboard::KeyboardController* controller = 734 keyboard::KeyboardController::GetInstance(); 735 aura::Window* keyboard_window = controller->proxy()->GetKeyboardWindow(); 736 keyboard_container->AddChild(keyboard_window); 737 keyboard_window->set_owned_by_parent(false); 738 keyboard_window->SetBounds(keyboard::KeyboardBoundsFromWindowBounds( 739 keyboard_container->bounds(), 100)); 740 keyboard_window->Show(); 741 742 gfx::Rect before = ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(); 743 744 // Notify keyboard bounds changing. 745 controller->NotifyKeyboardBoundsChanging( 746 controller->proxy()->GetKeyboardWindow()->bounds()); 747 748 if (!keyboard::IsKeyboardOverscrollEnabled()) { 749 gfx::Rect after = ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(); 750 EXPECT_LT(after, before); 751 } 752 753 // Mock a login user profile change to reinitialize the keyboard. 754 ash::Shell::GetInstance()->OnLoginUserProfilePrepared(); 755 EXPECT_EQ(ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(), before); 756 } 757 758 // Ensure that system modal dialogs do not block events targeted at the virtual 759 // keyboard. 760 TEST_F(VirtualKeyboardRootWindowControllerTest, ClickWithActiveModalDialog) { 761 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 762 aura::Window* keyboard_container = 763 Shell::GetContainer(root_window, kShellWindowId_VirtualKeyboardContainer); 764 ASSERT_TRUE(keyboard_container); 765 keyboard_container->Show(); 766 767 aura::Window* keyboard_window = keyboard::KeyboardController::GetInstance()-> 768 proxy()->GetKeyboardWindow(); 769 keyboard_container->AddChild(keyboard_window); 770 keyboard_window->set_owned_by_parent(false); 771 keyboard_window->SetBounds(keyboard::KeyboardBoundsFromWindowBounds( 772 keyboard_container->bounds(), 100)); 773 774 ui::test::TestEventHandler handler; 775 root_window->AddPreTargetHandler(&handler); 776 aura::test::EventGenerator root_window_event_generator(root_window); 777 aura::test::EventGenerator keyboard_event_generator(root_window, 778 keyboard_window); 779 780 views::Widget* modal_widget = 781 CreateModalWidget(gfx::Rect(300, 10, 100, 100)); 782 783 // Verify that mouse events to the root window are block with a visble modal 784 // dialog. 785 root_window_event_generator.ClickLeftButton(); 786 EXPECT_EQ(0, handler.num_mouse_events()); 787 788 // Verify that event dispatch to the virtual keyboard is unblocked. 789 keyboard_event_generator.ClickLeftButton(); 790 EXPECT_EQ(1, handler.num_mouse_events() / 2); 791 792 modal_widget->Close(); 793 794 // Verify that mouse events are now unblocked to the root window. 795 root_window_event_generator.ClickLeftButton(); 796 EXPECT_EQ(2, handler.num_mouse_events() / 2); 797 root_window->RemovePreTargetHandler(&handler); 798 } 799 800 // Ensure that the visible area for scrolling the text caret excludes the 801 // region occluded by the on-screen keyboard. 802 TEST_F(VirtualKeyboardRootWindowControllerTest, EnsureCaretInWorkArea) { 803 keyboard::KeyboardController* keyboard_controller = 804 keyboard::KeyboardController::GetInstance(); 805 keyboard::KeyboardControllerProxy* proxy = keyboard_controller->proxy(); 806 807 MockTextInputClient text_input_client; 808 ui::InputMethod* input_method = proxy->GetInputMethod(); 809 ASSERT_TRUE(input_method); 810 if (switches::IsTextInputFocusManagerEnabled()) { 811 ui::TextInputFocusManager::GetInstance()->FocusTextInputClient( 812 &text_input_client); 813 } else { 814 input_method->SetFocusedTextInputClient(&text_input_client); 815 } 816 817 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 818 aura::Window* keyboard_container = 819 Shell::GetContainer(root_window, kShellWindowId_VirtualKeyboardContainer); 820 ASSERT_TRUE(keyboard_container); 821 keyboard_container->Show(); 822 823 const int keyboard_height = 100; 824 aura::Window* keyboard_window =proxy->GetKeyboardWindow(); 825 keyboard_container->AddChild(keyboard_window); 826 keyboard_window->set_owned_by_parent(false); 827 keyboard_window->SetBounds(keyboard::KeyboardBoundsFromWindowBounds( 828 keyboard_container->bounds(), keyboard_height)); 829 830 proxy->EnsureCaretInWorkArea(); 831 ASSERT_EQ(keyboard_container->bounds().width(), 832 text_input_client.visible_rect().width()); 833 ASSERT_EQ(keyboard_container->bounds().height() - keyboard_height, 834 text_input_client.visible_rect().height()); 835 836 if (switches::IsTextInputFocusManagerEnabled()) { 837 ui::TextInputFocusManager::GetInstance()->BlurTextInputClient( 838 &text_input_client); 839 } else { 840 input_method->SetFocusedTextInputClient(NULL); 841 } 842 } 843 844 } // namespace test 845 } // namespace ash 846