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/shell.h" 6 7 #include <algorithm> 8 #include <vector> 9 10 #include "ash/ash_switches.h" 11 #include "ash/desktop_background/desktop_background_widget_controller.h" 12 #include "ash/display/mouse_cursor_event_filter.h" 13 #include "ash/drag_drop/drag_drop_controller.h" 14 #include "ash/launcher/launcher.h" 15 #include "ash/root_window_controller.h" 16 #include "ash/session_state_delegate.h" 17 #include "ash/shelf/shelf_layout_manager.h" 18 #include "ash/shelf/shelf_widget.h" 19 #include "ash/shell_delegate.h" 20 #include "ash/shell_window_ids.h" 21 #include "ash/test/ash_test_base.h" 22 #include "ash/test/shell_test_api.h" 23 #include "ash/wm/root_window_layout_manager.h" 24 #include "ash/wm/window_util.h" 25 #include "base/strings/utf_string_conversions.h" 26 #include "ui/aura/client/aura_constants.h" 27 #include "ui/aura/env.h" 28 #include "ui/aura/root_window.h" 29 #include "ui/aura/test/event_generator.h" 30 #include "ui/aura/test/test_event_handler.h" 31 #include "ui/aura/window.h" 32 #include "ui/base/models/simple_menu_model.h" 33 #include "ui/events/test/events_test_utils.h" 34 #include "ui/gfx/size.h" 35 #include "ui/views/controls/menu/menu_controller.h" 36 #include "ui/views/controls/menu/menu_runner.h" 37 #include "ui/views/widget/widget.h" 38 #include "ui/views/widget/widget_delegate.h" 39 #include "ui/views/window/dialog_delegate.h" 40 41 using aura::RootWindow; 42 43 namespace ash { 44 45 namespace { 46 47 aura::Window* GetDefaultContainer() { 48 return Shell::GetContainer( 49 Shell::GetPrimaryRootWindow(), 50 internal::kShellWindowId_DefaultContainer); 51 } 52 53 aura::Window* GetAlwaysOnTopContainer() { 54 return Shell::GetContainer( 55 Shell::GetPrimaryRootWindow(), 56 internal::kShellWindowId_AlwaysOnTopContainer); 57 } 58 59 // Expect ALL the containers! 60 void ExpectAllContainers() { 61 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 62 EXPECT_TRUE(Shell::GetContainer( 63 root_window, internal::kShellWindowId_DesktopBackgroundContainer)); 64 EXPECT_TRUE(Shell::GetContainer( 65 root_window, internal::kShellWindowId_DefaultContainer)); 66 EXPECT_TRUE(Shell::GetContainer( 67 root_window, internal::kShellWindowId_AlwaysOnTopContainer)); 68 EXPECT_TRUE(Shell::GetContainer( 69 root_window, internal::kShellWindowId_PanelContainer)); 70 EXPECT_TRUE(Shell::GetContainer( 71 root_window, internal::kShellWindowId_ShelfContainer)); 72 EXPECT_TRUE(Shell::GetContainer( 73 root_window, internal::kShellWindowId_SystemModalContainer)); 74 EXPECT_TRUE(Shell::GetContainer( 75 root_window, internal::kShellWindowId_LockScreenBackgroundContainer)); 76 EXPECT_TRUE(Shell::GetContainer( 77 root_window, internal::kShellWindowId_LockScreenContainer)); 78 EXPECT_TRUE(Shell::GetContainer( 79 root_window, internal::kShellWindowId_LockSystemModalContainer)); 80 EXPECT_TRUE(Shell::GetContainer( 81 root_window, internal::kShellWindowId_StatusContainer)); 82 EXPECT_TRUE(Shell::GetContainer( 83 root_window, internal::kShellWindowId_MenuContainer)); 84 EXPECT_TRUE(Shell::GetContainer( 85 root_window, internal::kShellWindowId_DragImageAndTooltipContainer)); 86 EXPECT_TRUE(Shell::GetContainer( 87 root_window, internal::kShellWindowId_SettingBubbleContainer)); 88 EXPECT_TRUE(Shell::GetContainer( 89 root_window, internal::kShellWindowId_OverlayContainer)); 90 } 91 92 class ModalWindow : public views::WidgetDelegateView { 93 public: 94 ModalWindow() {} 95 virtual ~ModalWindow() {} 96 97 // Overridden from views::WidgetDelegate: 98 virtual views::View* GetContentsView() OVERRIDE { 99 return this; 100 } 101 virtual bool CanResize() const OVERRIDE { 102 return true; 103 } 104 virtual base::string16 GetWindowTitle() const OVERRIDE { 105 return ASCIIToUTF16("Modal Window"); 106 } 107 virtual ui::ModalType GetModalType() const OVERRIDE { 108 return ui::MODAL_TYPE_SYSTEM; 109 } 110 111 private: 112 DISALLOW_COPY_AND_ASSIGN(ModalWindow); 113 }; 114 115 class SimpleMenuDelegate : public ui::SimpleMenuModel::Delegate { 116 public: 117 SimpleMenuDelegate() {} 118 virtual ~SimpleMenuDelegate() {} 119 120 virtual bool IsCommandIdChecked(int command_id) const OVERRIDE { 121 return false; 122 } 123 124 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE { 125 return true; 126 } 127 128 virtual bool GetAcceleratorForCommandId( 129 int command_id, 130 ui::Accelerator* accelerator) OVERRIDE { 131 return false; 132 } 133 134 virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE { 135 } 136 137 private: 138 DISALLOW_COPY_AND_ASSIGN(SimpleMenuDelegate); 139 }; 140 141 } // namespace 142 143 class ShellTest : public test::AshTestBase { 144 public: 145 views::Widget* CreateTestWindow(views::Widget::InitParams params) { 146 views::Widget* widget = new views::Widget; 147 params.context = CurrentContext(); 148 widget->Init(params); 149 return widget; 150 } 151 152 void TestCreateWindow(views::Widget::InitParams::Type type, 153 bool always_on_top, 154 aura::Window* expected_container) { 155 views::Widget::InitParams widget_params(type); 156 widget_params.keep_on_top = always_on_top; 157 158 views::Widget* widget = CreateTestWindow(widget_params); 159 widget->Show(); 160 161 EXPECT_TRUE( 162 expected_container->Contains(widget->GetNativeWindow()->parent())) << 163 "TestCreateWindow: type=" << type << ", always_on_top=" << 164 always_on_top; 165 166 widget->Close(); 167 } 168 169 void LockScreenAndVerifyMenuClosed() { 170 // Verify a menu is open before locking. 171 views::MenuController* menu_controller = 172 views::MenuController::GetActiveInstance(); 173 DCHECK(menu_controller); 174 EXPECT_EQ(views::MenuController::EXIT_NONE, menu_controller->exit_type()); 175 176 // Create a LockScreen window. 177 views::Widget::InitParams widget_params( 178 views::Widget::InitParams::TYPE_WINDOW); 179 SessionStateDelegate* delegate = 180 Shell::GetInstance()->session_state_delegate(); 181 delegate->LockScreen(); 182 views::Widget* lock_widget = CreateTestWindow(widget_params); 183 ash::Shell::GetContainer( 184 Shell::GetPrimaryRootWindow(), 185 ash::internal::kShellWindowId_LockScreenContainer)-> 186 AddChild(lock_widget->GetNativeView()); 187 lock_widget->Show(); 188 EXPECT_TRUE(delegate->IsScreenLocked()); 189 EXPECT_TRUE(lock_widget->GetNativeView()->HasFocus()); 190 191 // Verify menu is closed. 192 EXPECT_NE(views::MenuController::EXIT_NONE, menu_controller->exit_type()); 193 lock_widget->Close(); 194 delegate->UnlockScreen(); 195 196 // In case the menu wasn't closed, cancel the menu to exit the nested menu 197 // run loop so that the test will not time out. 198 menu_controller->CancelAll(); 199 } 200 }; 201 202 TEST_F(ShellTest, CreateWindow) { 203 // Normal window should be created in default container. 204 TestCreateWindow(views::Widget::InitParams::TYPE_WINDOW, 205 false, // always_on_top 206 GetDefaultContainer()); 207 TestCreateWindow(views::Widget::InitParams::TYPE_POPUP, 208 false, // always_on_top 209 GetDefaultContainer()); 210 211 // Always-on-top window and popup are created in always-on-top container. 212 TestCreateWindow(views::Widget::InitParams::TYPE_WINDOW, 213 true, // always_on_top 214 GetAlwaysOnTopContainer()); 215 TestCreateWindow(views::Widget::InitParams::TYPE_POPUP, 216 true, // always_on_top 217 GetAlwaysOnTopContainer()); 218 } 219 220 TEST_F(ShellTest, ChangeAlwaysOnTop) { 221 views::Widget::InitParams widget_params( 222 views::Widget::InitParams::TYPE_WINDOW); 223 224 // Creates a normal window 225 views::Widget* widget = CreateTestWindow(widget_params); 226 widget->Show(); 227 228 // It should be in default container. 229 EXPECT_TRUE(GetDefaultContainer()->Contains( 230 widget->GetNativeWindow()->parent())); 231 232 // Flip always-on-top flag. 233 widget->SetAlwaysOnTop(true); 234 // And it should in always on top container now. 235 EXPECT_EQ(GetAlwaysOnTopContainer(), widget->GetNativeWindow()->parent()); 236 237 // Flip always-on-top flag. 238 widget->SetAlwaysOnTop(false); 239 // It should go back to default container. 240 EXPECT_TRUE(GetDefaultContainer()->Contains( 241 widget->GetNativeWindow()->parent())); 242 243 // Set the same always-on-top flag again. 244 widget->SetAlwaysOnTop(false); 245 // Should have no effect and we are still in the default container. 246 EXPECT_TRUE(GetDefaultContainer()->Contains( 247 widget->GetNativeWindow()->parent())); 248 249 widget->Close(); 250 } 251 252 TEST_F(ShellTest, CreateModalWindow) { 253 views::Widget::InitParams widget_params( 254 views::Widget::InitParams::TYPE_WINDOW); 255 256 // Create a normal window. 257 views::Widget* widget = CreateTestWindow(widget_params); 258 widget->Show(); 259 260 // It should be in default container. 261 EXPECT_TRUE(GetDefaultContainer()->Contains( 262 widget->GetNativeWindow()->parent())); 263 264 // Create a modal window. 265 views::Widget* modal_widget = views::Widget::CreateWindowWithParent( 266 new ModalWindow(), widget->GetNativeView()); 267 modal_widget->Show(); 268 269 // It should be in modal container. 270 aura::Window* modal_container = Shell::GetContainer( 271 Shell::GetPrimaryRootWindow(), 272 internal::kShellWindowId_SystemModalContainer); 273 EXPECT_EQ(modal_container, modal_widget->GetNativeWindow()->parent()); 274 275 modal_widget->Close(); 276 widget->Close(); 277 } 278 279 class TestModalDialogDelegate : public views::DialogDelegateView { 280 public: 281 TestModalDialogDelegate() {} 282 283 // Overridden from views::WidgetDelegate: 284 virtual ui::ModalType GetModalType() const OVERRIDE { 285 return ui::MODAL_TYPE_SYSTEM; 286 } 287 }; 288 289 TEST_F(ShellTest, CreateLockScreenModalWindow) { 290 views::Widget::InitParams widget_params( 291 views::Widget::InitParams::TYPE_WINDOW); 292 293 // Create a normal window. 294 views::Widget* widget = CreateTestWindow(widget_params); 295 widget->Show(); 296 EXPECT_TRUE(widget->GetNativeView()->HasFocus()); 297 298 // It should be in default container. 299 EXPECT_TRUE(GetDefaultContainer()->Contains( 300 widget->GetNativeWindow()->parent())); 301 302 Shell::GetInstance()->session_state_delegate()->LockScreen(); 303 // Create a LockScreen window. 304 views::Widget* lock_widget = CreateTestWindow(widget_params); 305 ash::Shell::GetContainer( 306 Shell::GetPrimaryRootWindow(), 307 ash::internal::kShellWindowId_LockScreenContainer)-> 308 AddChild(lock_widget->GetNativeView()); 309 lock_widget->Show(); 310 EXPECT_TRUE(lock_widget->GetNativeView()->HasFocus()); 311 312 // It should be in LockScreen container. 313 aura::Window* lock_screen = Shell::GetContainer( 314 Shell::GetPrimaryRootWindow(), 315 ash::internal::kShellWindowId_LockScreenContainer); 316 EXPECT_EQ(lock_screen, lock_widget->GetNativeWindow()->parent()); 317 318 // Create a modal window with a lock window as parent. 319 views::Widget* lock_modal_widget = views::Widget::CreateWindowWithParent( 320 new ModalWindow(), lock_widget->GetNativeView()); 321 lock_modal_widget->Show(); 322 EXPECT_TRUE(lock_modal_widget->GetNativeView()->HasFocus()); 323 324 // It should be in LockScreen modal container. 325 aura::Window* lock_modal_container = Shell::GetContainer( 326 Shell::GetPrimaryRootWindow(), 327 ash::internal::kShellWindowId_LockSystemModalContainer); 328 EXPECT_EQ(lock_modal_container, 329 lock_modal_widget->GetNativeWindow()->parent()); 330 331 // Create a modal window with a normal window as parent. 332 views::Widget* modal_widget = views::Widget::CreateWindowWithParent( 333 new ModalWindow(), widget->GetNativeView()); 334 modal_widget->Show(); 335 // Window on lock screen shouldn't lost focus. 336 EXPECT_FALSE(modal_widget->GetNativeView()->HasFocus()); 337 EXPECT_TRUE(lock_modal_widget->GetNativeView()->HasFocus()); 338 339 // It should be in non-LockScreen modal container. 340 aura::Window* modal_container = Shell::GetContainer( 341 Shell::GetPrimaryRootWindow(), 342 ash::internal::kShellWindowId_SystemModalContainer); 343 EXPECT_EQ(modal_container, modal_widget->GetNativeWindow()->parent()); 344 345 // Modal dialog without parent, caused crash see crbug.com/226141 346 views::Widget* modal_dialog = views::DialogDelegate::CreateDialogWidget( 347 new TestModalDialogDelegate(), CurrentContext(), NULL); 348 349 modal_dialog->Show(); 350 EXPECT_FALSE(modal_dialog->GetNativeView()->HasFocus()); 351 EXPECT_TRUE(lock_modal_widget->GetNativeView()->HasFocus()); 352 353 modal_dialog->Close(); 354 modal_widget->Close(); 355 modal_widget->Close(); 356 lock_modal_widget->Close(); 357 lock_widget->Close(); 358 widget->Close(); 359 } 360 361 TEST_F(ShellTest, IsScreenLocked) { 362 SessionStateDelegate* delegate = 363 Shell::GetInstance()->session_state_delegate(); 364 delegate->LockScreen(); 365 EXPECT_TRUE(delegate->IsScreenLocked()); 366 delegate->UnlockScreen(); 367 EXPECT_FALSE(delegate->IsScreenLocked()); 368 } 369 370 TEST_F(ShellTest, LockScreenClosesActiveMenu) { 371 SimpleMenuDelegate menu_delegate; 372 scoped_ptr<ui::SimpleMenuModel> menu_model( 373 new ui::SimpleMenuModel(&menu_delegate)); 374 menu_model->AddItem(0, ASCIIToUTF16("Menu item")); 375 views::Widget* widget = ash::Shell::GetPrimaryRootWindowController()-> 376 wallpaper_controller()->widget(); 377 scoped_ptr<views::MenuRunner> menu_runner( 378 new views::MenuRunner(menu_model.get())); 379 380 // When MenuRunner runs a nested loop the LockScreenAndVerifyMenuClosed 381 // command will fire, check the menu state and ensure the nested menu loop 382 // is exited so that the test will terminate. 383 base::MessageLoopForUI::current()->PostTask(FROM_HERE, 384 base::Bind(&ShellTest::LockScreenAndVerifyMenuClosed, 385 base::Unretained(this))); 386 387 EXPECT_EQ(views::MenuRunner::NORMAL_EXIT, 388 menu_runner->RunMenuAt(widget, NULL, gfx::Rect(), 389 views::MenuItemView::TOPLEFT, ui::MENU_SOURCE_MOUSE, 390 views::MenuRunner::CONTEXT_MENU)); 391 } 392 393 TEST_F(ShellTest, ManagedWindowModeBasics) { 394 // We start with the usual window containers. 395 ExpectAllContainers(); 396 // Shelf is visible. 397 ShelfWidget* launcher_widget = Launcher::ForPrimaryDisplay()->shelf_widget(); 398 EXPECT_TRUE(launcher_widget->IsVisible()); 399 // Shelf is at bottom-left of screen. 400 EXPECT_EQ(0, launcher_widget->GetWindowBoundsInScreen().x()); 401 EXPECT_EQ(Shell::GetPrimaryRootWindow()->GetDispatcher()->host()-> 402 GetBounds().height(), 403 launcher_widget->GetWindowBoundsInScreen().bottom()); 404 // We have a desktop background but not a bare layer. 405 // TODO (antrim): enable once we find out why it fails component build. 406 // internal::DesktopBackgroundWidgetController* background = 407 // Shell::GetPrimaryRootWindow()-> 408 // GetProperty(internal::kWindowDesktopComponent); 409 // EXPECT_TRUE(background); 410 // EXPECT_TRUE(background->widget()); 411 // EXPECT_FALSE(background->layer()); 412 413 // Create a normal window. It is not maximized. 414 views::Widget::InitParams widget_params( 415 views::Widget::InitParams::TYPE_WINDOW); 416 widget_params.bounds.SetRect(11, 22, 300, 400); 417 views::Widget* widget = CreateTestWindow(widget_params); 418 widget->Show(); 419 EXPECT_FALSE(widget->IsMaximized()); 420 421 // Clean up. 422 widget->Close(); 423 } 424 425 TEST_F(ShellTest, FullscreenWindowHidesShelf) { 426 ExpectAllContainers(); 427 428 // Create a normal window. It is not maximized. 429 views::Widget::InitParams widget_params( 430 views::Widget::InitParams::TYPE_WINDOW); 431 widget_params.bounds.SetRect(11, 22, 300, 400); 432 views::Widget* widget = CreateTestWindow(widget_params); 433 widget->Show(); 434 EXPECT_FALSE(widget->IsMaximized()); 435 436 // Shelf defaults to visible. 437 EXPECT_EQ( 438 SHELF_VISIBLE, 439 Shell::GetPrimaryRootWindowController()-> 440 GetShelfLayoutManager()->visibility_state()); 441 442 // Fullscreen window hides it. 443 widget->SetFullscreen(true); 444 EXPECT_EQ( 445 SHELF_HIDDEN, 446 Shell::GetPrimaryRootWindowController()-> 447 GetShelfLayoutManager()->visibility_state()); 448 449 // Restoring the window restores it. 450 widget->Restore(); 451 EXPECT_EQ( 452 SHELF_VISIBLE, 453 Shell::GetPrimaryRootWindowController()-> 454 GetShelfLayoutManager()->visibility_state()); 455 456 // Clean up. 457 widget->Close(); 458 } 459 460 // Various assertions around SetShelfAutoHideBehavior() and 461 // GetShelfAutoHideBehavior(). 462 TEST_F(ShellTest, ToggleAutoHide) { 463 scoped_ptr<aura::Window> window(new aura::Window(NULL)); 464 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 465 window->SetType(aura::client::WINDOW_TYPE_NORMAL); 466 window->Init(ui::LAYER_TEXTURED); 467 ParentWindowInPrimaryRootWindow(window.get()); 468 window->Show(); 469 wm::ActivateWindow(window.get()); 470 471 Shell* shell = Shell::GetInstance(); 472 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 473 shell->SetShelfAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, 474 root_window); 475 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, 476 shell->GetShelfAutoHideBehavior(root_window)); 477 shell->SetShelfAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, 478 root_window); 479 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, 480 shell->GetShelfAutoHideBehavior(root_window)); 481 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 482 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, 483 shell->GetShelfAutoHideBehavior(root_window)); 484 shell->SetShelfAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, 485 root_window); 486 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, 487 shell->GetShelfAutoHideBehavior(root_window)); 488 shell->SetShelfAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, 489 root_window); 490 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, 491 shell->GetShelfAutoHideBehavior(root_window)); 492 } 493 494 TEST_F(ShellTest, TestPreTargetHandlerOrder) { 495 Shell* shell = Shell::GetInstance(); 496 ui::EventTargetTestApi test_api(shell); 497 test::ShellTestApi shell_test_api(shell); 498 499 const ui::EventHandlerList& handlers = test_api.pre_target_handlers(); 500 EXPECT_EQ(handlers[0], shell->mouse_cursor_filter()); 501 EXPECT_EQ(handlers[1], shell_test_api.drag_drop_controller()); 502 } 503 504 // Verifies an EventHandler added to Env gets notified from EventGenerator. 505 TEST_F(ShellTest, EnvPreTargetHandler) { 506 aura::test::TestEventHandler event_handler; 507 aura::Env::GetInstance()->AddPreTargetHandler(&event_handler); 508 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 509 generator.MoveMouseBy(1, 1); 510 EXPECT_NE(0, event_handler.num_mouse_events()); 511 aura::Env::GetInstance()->RemovePreTargetHandler(&event_handler); 512 } 513 514 // This verifies WindowObservers are removed when a window is destroyed after 515 // the Shell is destroyed. This scenario (aura::Windows being deleted after the 516 // Shell) occurs if someone is holding a reference to an unparented Window, as 517 // is the case with a RenderWidgetHostViewAura that isn't on screen. As long as 518 // everything is ok, we won't crash. If there is a bug, window's destructor will 519 // notify some deleted object (say VideoDetector or ActivationController) and 520 // this will crash. 521 class ShellTest2 : public test::AshTestBase { 522 public: 523 ShellTest2() {} 524 virtual ~ShellTest2() {} 525 526 protected: 527 scoped_ptr<aura::Window> window_; 528 529 private: 530 DISALLOW_COPY_AND_ASSIGN(ShellTest2); 531 }; 532 533 TEST_F(ShellTest2, DontCrashWhenWindowDeleted) { 534 window_.reset(new aura::Window(NULL)); 535 window_->Init(ui::LAYER_NOT_DRAWN); 536 } 537 538 } // namespace ash 539