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_window_ids.h" 20 #include "ash/test/ash_test_base.h" 21 #include "ash/test/shell_test_api.h" 22 #include "ash/wm/root_window_layout_manager.h" 23 #include "ash/wm/window_util.h" 24 #include "base/strings/utf_string_conversions.h" 25 #include "ui/aura/client/aura_constants.h" 26 #include "ui/aura/root_window.h" 27 #include "ui/aura/window.h" 28 #include "ui/gfx/size.h" 29 #include "ui/views/widget/widget.h" 30 #include "ui/views/widget/widget_delegate.h" 31 #include "ui/views/window/dialog_delegate.h" 32 33 using aura::RootWindow; 34 35 namespace ash { 36 37 namespace { 38 39 aura::Window* GetDefaultContainer() { 40 return Shell::GetContainer( 41 Shell::GetPrimaryRootWindow(), 42 internal::kShellWindowId_DefaultContainer); 43 } 44 45 aura::Window* GetAlwaysOnTopContainer() { 46 return Shell::GetContainer( 47 Shell::GetPrimaryRootWindow(), 48 internal::kShellWindowId_AlwaysOnTopContainer); 49 } 50 51 // Expect ALL the containers! 52 void ExpectAllContainers() { 53 aura::RootWindow* root_window = Shell::GetPrimaryRootWindow(); 54 EXPECT_TRUE(Shell::GetContainer( 55 root_window, internal::kShellWindowId_DesktopBackgroundContainer)); 56 EXPECT_TRUE(Shell::GetContainer( 57 root_window, internal::kShellWindowId_DefaultContainer)); 58 EXPECT_TRUE(Shell::GetContainer( 59 root_window, internal::kShellWindowId_AlwaysOnTopContainer)); 60 EXPECT_TRUE(Shell::GetContainer( 61 root_window, internal::kShellWindowId_PanelContainer)); 62 EXPECT_TRUE(Shell::GetContainer( 63 root_window, internal::kShellWindowId_ShelfContainer)); 64 EXPECT_TRUE(Shell::GetContainer( 65 root_window, internal::kShellWindowId_SystemModalContainer)); 66 EXPECT_TRUE(Shell::GetContainer( 67 root_window, internal::kShellWindowId_LockScreenBackgroundContainer)); 68 EXPECT_TRUE(Shell::GetContainer( 69 root_window, internal::kShellWindowId_LockScreenContainer)); 70 EXPECT_TRUE(Shell::GetContainer( 71 root_window, internal::kShellWindowId_LockSystemModalContainer)); 72 EXPECT_TRUE(Shell::GetContainer( 73 root_window, internal::kShellWindowId_StatusContainer)); 74 EXPECT_TRUE(Shell::GetContainer( 75 root_window, internal::kShellWindowId_MenuContainer)); 76 EXPECT_TRUE(Shell::GetContainer( 77 root_window, internal::kShellWindowId_DragImageAndTooltipContainer)); 78 EXPECT_TRUE(Shell::GetContainer( 79 root_window, internal::kShellWindowId_SettingBubbleContainer)); 80 EXPECT_TRUE(Shell::GetContainer( 81 root_window, internal::kShellWindowId_OverlayContainer)); 82 } 83 84 class ModalWindow : public views::WidgetDelegateView { 85 public: 86 ModalWindow() {} 87 virtual ~ModalWindow() {} 88 89 // Overridden from views::WidgetDelegate: 90 virtual views::View* GetContentsView() OVERRIDE { 91 return this; 92 } 93 virtual bool CanResize() const OVERRIDE { 94 return true; 95 } 96 virtual base::string16 GetWindowTitle() const OVERRIDE { 97 return ASCIIToUTF16("Modal Window"); 98 } 99 virtual ui::ModalType GetModalType() const OVERRIDE { 100 return ui::MODAL_TYPE_SYSTEM; 101 } 102 103 private: 104 DISALLOW_COPY_AND_ASSIGN(ModalWindow); 105 }; 106 107 } // namespace 108 109 class ShellTest : public test::AshTestBase { 110 public: 111 views::Widget* CreateTestWindow(views::Widget::InitParams params) { 112 views::Widget* widget = new views::Widget; 113 params.context = CurrentContext(); 114 widget->Init(params); 115 return widget; 116 } 117 118 void TestCreateWindow(views::Widget::InitParams::Type type, 119 bool always_on_top, 120 aura::Window* expected_container) { 121 views::Widget::InitParams widget_params(type); 122 widget_params.keep_on_top = always_on_top; 123 124 views::Widget* widget = CreateTestWindow(widget_params); 125 widget->Show(); 126 127 EXPECT_TRUE( 128 expected_container->Contains(widget->GetNativeWindow()->parent())) << 129 "TestCreateWindow: type=" << type << ", always_on_top=" << 130 always_on_top; 131 132 widget->Close(); 133 } 134 135 }; 136 137 TEST_F(ShellTest, CreateWindow) { 138 // Normal window should be created in default container. 139 TestCreateWindow(views::Widget::InitParams::TYPE_WINDOW, 140 false, // always_on_top 141 GetDefaultContainer()); 142 TestCreateWindow(views::Widget::InitParams::TYPE_POPUP, 143 false, // always_on_top 144 GetDefaultContainer()); 145 146 // Always-on-top window and popup are created in always-on-top container. 147 TestCreateWindow(views::Widget::InitParams::TYPE_WINDOW, 148 true, // always_on_top 149 GetAlwaysOnTopContainer()); 150 TestCreateWindow(views::Widget::InitParams::TYPE_POPUP, 151 true, // always_on_top 152 GetAlwaysOnTopContainer()); 153 } 154 155 TEST_F(ShellTest, ChangeAlwaysOnTop) { 156 views::Widget::InitParams widget_params( 157 views::Widget::InitParams::TYPE_WINDOW); 158 159 // Creates a normal window 160 views::Widget* widget = CreateTestWindow(widget_params); 161 widget->Show(); 162 163 // It should be in default container. 164 EXPECT_TRUE(GetDefaultContainer()->Contains( 165 widget->GetNativeWindow()->parent())); 166 167 // Flip always-on-top flag. 168 widget->SetAlwaysOnTop(true); 169 // And it should in always on top container now. 170 EXPECT_EQ(GetAlwaysOnTopContainer(), widget->GetNativeWindow()->parent()); 171 172 // Flip always-on-top flag. 173 widget->SetAlwaysOnTop(false); 174 // It should go back to default container. 175 EXPECT_TRUE(GetDefaultContainer()->Contains( 176 widget->GetNativeWindow()->parent())); 177 178 // Set the same always-on-top flag again. 179 widget->SetAlwaysOnTop(false); 180 // Should have no effect and we are still in the default container. 181 EXPECT_TRUE(GetDefaultContainer()->Contains( 182 widget->GetNativeWindow()->parent())); 183 184 widget->Close(); 185 } 186 187 TEST_F(ShellTest, CreateModalWindow) { 188 views::Widget::InitParams widget_params( 189 views::Widget::InitParams::TYPE_WINDOW); 190 191 // Create a normal window. 192 views::Widget* widget = CreateTestWindow(widget_params); 193 widget->Show(); 194 195 // It should be in default container. 196 EXPECT_TRUE(GetDefaultContainer()->Contains( 197 widget->GetNativeWindow()->parent())); 198 199 // Create a modal window. 200 views::Widget* modal_widget = views::Widget::CreateWindowWithParent( 201 new ModalWindow(), widget->GetNativeView()); 202 modal_widget->Show(); 203 204 // It should be in modal container. 205 aura::Window* modal_container = Shell::GetContainer( 206 Shell::GetPrimaryRootWindow(), 207 internal::kShellWindowId_SystemModalContainer); 208 EXPECT_EQ(modal_container, modal_widget->GetNativeWindow()->parent()); 209 210 modal_widget->Close(); 211 widget->Close(); 212 } 213 214 class TestModalDialogDelegate : public views::DialogDelegateView { 215 public: 216 TestModalDialogDelegate() {} 217 218 // Overridden from views::WidgetDelegate: 219 virtual ui::ModalType GetModalType() const OVERRIDE { 220 return ui::MODAL_TYPE_SYSTEM; 221 } 222 }; 223 224 TEST_F(ShellTest, CreateLockScreenModalWindow) { 225 views::Widget::InitParams widget_params( 226 views::Widget::InitParams::TYPE_WINDOW); 227 228 // Create a normal window. 229 views::Widget* widget = CreateTestWindow(widget_params); 230 widget->Show(); 231 EXPECT_TRUE(widget->GetNativeView()->HasFocus()); 232 233 // It should be in default container. 234 EXPECT_TRUE(GetDefaultContainer()->Contains( 235 widget->GetNativeWindow()->parent())); 236 237 Shell::GetInstance()->session_state_delegate()->LockScreen(); 238 // Create a LockScreen window. 239 views::Widget* lock_widget = CreateTestWindow(widget_params); 240 ash::Shell::GetContainer( 241 Shell::GetPrimaryRootWindow(), 242 ash::internal::kShellWindowId_LockScreenContainer)-> 243 AddChild(lock_widget->GetNativeView()); 244 lock_widget->Show(); 245 EXPECT_TRUE(lock_widget->GetNativeView()->HasFocus()); 246 247 // It should be in LockScreen container. 248 aura::Window* lock_screen = Shell::GetContainer( 249 Shell::GetPrimaryRootWindow(), 250 ash::internal::kShellWindowId_LockScreenContainer); 251 EXPECT_EQ(lock_screen, lock_widget->GetNativeWindow()->parent()); 252 253 // Create a modal window with a lock window as parent. 254 views::Widget* lock_modal_widget = views::Widget::CreateWindowWithParent( 255 new ModalWindow(), lock_widget->GetNativeView()); 256 lock_modal_widget->Show(); 257 EXPECT_TRUE(lock_modal_widget->GetNativeView()->HasFocus()); 258 259 // It should be in LockScreen modal container. 260 aura::Window* lock_modal_container = Shell::GetContainer( 261 Shell::GetPrimaryRootWindow(), 262 ash::internal::kShellWindowId_LockSystemModalContainer); 263 EXPECT_EQ(lock_modal_container, 264 lock_modal_widget->GetNativeWindow()->parent()); 265 266 // Create a modal window with a normal window as parent. 267 views::Widget* modal_widget = views::Widget::CreateWindowWithParent( 268 new ModalWindow(), widget->GetNativeView()); 269 modal_widget->Show(); 270 // Window on lock screen shouldn't lost focus. 271 EXPECT_FALSE(modal_widget->GetNativeView()->HasFocus()); 272 EXPECT_TRUE(lock_modal_widget->GetNativeView()->HasFocus()); 273 274 // It should be in non-LockScreen modal container. 275 aura::Window* modal_container = Shell::GetContainer( 276 Shell::GetPrimaryRootWindow(), 277 ash::internal::kShellWindowId_SystemModalContainer); 278 EXPECT_EQ(modal_container, modal_widget->GetNativeWindow()->parent()); 279 280 // Modal dialog without parent, caused crash see crbug.com/226141 281 views::Widget* modal_dialog = views::DialogDelegate::CreateDialogWidget( 282 new TestModalDialogDelegate(), CurrentContext(), NULL); 283 284 modal_dialog->Show(); 285 EXPECT_FALSE(modal_dialog->GetNativeView()->HasFocus()); 286 EXPECT_TRUE(lock_modal_widget->GetNativeView()->HasFocus()); 287 288 modal_dialog->Close(); 289 modal_widget->Close(); 290 modal_widget->Close(); 291 lock_modal_widget->Close(); 292 lock_widget->Close(); 293 widget->Close(); 294 } 295 296 TEST_F(ShellTest, IsScreenLocked) { 297 SessionStateDelegate* delegate = 298 Shell::GetInstance()->session_state_delegate(); 299 delegate->LockScreen(); 300 EXPECT_TRUE(delegate->IsScreenLocked()); 301 delegate->UnlockScreen(); 302 EXPECT_FALSE(delegate->IsScreenLocked()); 303 } 304 305 // Fails on Mac, see http://crbug.com/115662 306 #if defined(OS_MACOSX) 307 #define MAYBE_ManagedWindowModeBasics DISABLED_ManagedWindowModeBasics 308 #else 309 #define MAYBE_ManagedWindowModeBasics ManagedWindowModeBasics 310 #endif 311 TEST_F(ShellTest, MAYBE_ManagedWindowModeBasics) { 312 Shell* shell = Shell::GetInstance(); 313 Shell::TestApi test_api(shell); 314 315 // We start with the usual window containers. 316 ExpectAllContainers(); 317 // Launcher is visible. 318 ShelfWidget* launcher_widget = Launcher::ForPrimaryDisplay()->shelf_widget(); 319 EXPECT_TRUE(launcher_widget->IsVisible()); 320 // Launcher is at bottom-left of screen. 321 EXPECT_EQ(0, launcher_widget->GetWindowBoundsInScreen().x()); 322 EXPECT_EQ(Shell::GetPrimaryRootWindow()->GetHostSize().height(), 323 launcher_widget->GetWindowBoundsInScreen().bottom()); 324 // We have a desktop background but not a bare layer. 325 // TODO (antrim): enable once we find out why it fails component build. 326 // internal::DesktopBackgroundWidgetController* background = 327 // Shell::GetPrimaryRootWindow()-> 328 // GetProperty(internal::kWindowDesktopComponent); 329 // EXPECT_TRUE(background); 330 // EXPECT_TRUE(background->widget()); 331 // EXPECT_FALSE(background->layer()); 332 333 // Create a normal window. It is not maximized. 334 views::Widget::InitParams widget_params( 335 views::Widget::InitParams::TYPE_WINDOW); 336 widget_params.bounds.SetRect(11, 22, 300, 400); 337 views::Widget* widget = CreateTestWindow(widget_params); 338 widget->Show(); 339 EXPECT_FALSE(widget->IsMaximized()); 340 341 // Clean up. 342 widget->Close(); 343 } 344 345 TEST_F(ShellTest, FullscreenWindowHidesShelf) { 346 ExpectAllContainers(); 347 348 // Create a normal window. It is not maximized. 349 views::Widget::InitParams widget_params( 350 views::Widget::InitParams::TYPE_WINDOW); 351 widget_params.bounds.SetRect(11, 22, 300, 400); 352 views::Widget* widget = CreateTestWindow(widget_params); 353 widget->Show(); 354 EXPECT_FALSE(widget->IsMaximized()); 355 356 // Shelf defaults to visible. 357 EXPECT_EQ( 358 SHELF_VISIBLE, 359 Shell::GetPrimaryRootWindowController()-> 360 GetShelfLayoutManager()->visibility_state()); 361 362 // Fullscreen window hides it. 363 widget->SetFullscreen(true); 364 EXPECT_EQ( 365 SHELF_HIDDEN, 366 Shell::GetPrimaryRootWindowController()-> 367 GetShelfLayoutManager()->visibility_state()); 368 369 // Restoring the window restores it. 370 widget->Restore(); 371 EXPECT_EQ( 372 SHELF_VISIBLE, 373 Shell::GetPrimaryRootWindowController()-> 374 GetShelfLayoutManager()->visibility_state()); 375 376 // Clean up. 377 widget->Close(); 378 } 379 380 namespace { 381 382 // Builds the list of parents from |window| to the root. The returned vector is 383 // in reverse order (|window| is first). 384 std::vector<aura::Window*> BuildPathToRoot(aura::Window* window) { 385 std::vector<aura::Window*> results; 386 while (window) { 387 results.push_back(window); 388 window = window->parent(); 389 } 390 return results; 391 } 392 393 } // namespace 394 395 // Various assertions around SetShelfAutoHideBehavior() and 396 // GetShelfAutoHideBehavior(). 397 TEST_F(ShellTest, ToggleAutoHide) { 398 scoped_ptr<aura::Window> window(new aura::Window(NULL)); 399 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 400 window->SetType(aura::client::WINDOW_TYPE_NORMAL); 401 window->Init(ui::LAYER_TEXTURED); 402 SetDefaultParentByPrimaryRootWindow(window.get()); 403 window->Show(); 404 wm::ActivateWindow(window.get()); 405 406 Shell* shell = Shell::GetInstance(); 407 aura::RootWindow* root_window = Shell::GetPrimaryRootWindow(); 408 shell->SetShelfAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, 409 root_window); 410 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, 411 shell->GetShelfAutoHideBehavior(root_window)); 412 shell->SetShelfAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, 413 root_window); 414 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, 415 shell->GetShelfAutoHideBehavior(root_window)); 416 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 417 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, 418 shell->GetShelfAutoHideBehavior(root_window)); 419 shell->SetShelfAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, 420 root_window); 421 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, 422 shell->GetShelfAutoHideBehavior(root_window)); 423 shell->SetShelfAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, 424 root_window); 425 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, 426 shell->GetShelfAutoHideBehavior(root_window)); 427 } 428 429 TEST_F(ShellTest, TestPreTargetHandlerOrder) { 430 Shell* shell = Shell::GetInstance(); 431 Shell::TestApi test_api(shell); 432 test::ShellTestApi shell_test_api(shell); 433 434 const ui::EventHandlerList& handlers = test_api.pre_target_handlers(); 435 EXPECT_EQ(handlers[0], shell->mouse_cursor_filter()); 436 EXPECT_EQ(handlers[1], shell_test_api.drag_drop_controller()); 437 } 438 439 // This verifies WindowObservers are removed when a window is destroyed after 440 // the Shell is destroyed. This scenario (aura::Windows being deleted after the 441 // Shell) occurs if someone is holding a reference to an unparented Window, as 442 // is the case with a RenderWidgetHostViewAura that isn't on screen. As long as 443 // everything is ok, we won't crash. If there is a bug, window's destructor will 444 // notify some deleted object (say VideoDetector or ActivationController) and 445 // this will crash. 446 class ShellTest2 : public test::AshTestBase { 447 public: 448 ShellTest2() {} 449 virtual ~ShellTest2() {} 450 451 protected: 452 scoped_ptr<aura::Window> window_; 453 454 private: 455 DISALLOW_COPY_AND_ASSIGN(ShellTest2); 456 }; 457 458 TEST_F(ShellTest2, DontCrashWhenWindowDeleted) { 459 window_.reset(new aura::Window(NULL)); 460 window_->Init(ui::LAYER_NOT_DRAWN); 461 } 462 463 } // namespace ash 464