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