1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 6 7 #include "base/bind.h" 8 #include "ui/aura/client/aura_constants.h" 9 #include "ui/aura/client/cursor_client.h" 10 #include "ui/aura/client/window_tree_client.h" 11 #include "ui/aura/test/event_generator.h" 12 #include "ui/aura/test/test_window_delegate.h" 13 #include "ui/aura/window.h" 14 #include "ui/aura/window_tree_host.h" 15 #include "ui/views/test/views_test_base.h" 16 #include "ui/views/test/widget_test.h" 17 #include "ui/views/widget/widget.h" 18 #include "ui/wm/public/dispatcher_client.h" 19 20 namespace views { 21 namespace test { 22 23 typedef ViewsTestBase DesktopNativeWidgetAuraTest; 24 25 // Verifies creating a Widget with a parent that is not in a RootWindow doesn't 26 // crash. 27 TEST_F(DesktopNativeWidgetAuraTest, CreateWithParentNotInRootWindow) { 28 scoped_ptr<aura::Window> window(new aura::Window(NULL)); 29 Widget widget; 30 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); 31 params.bounds = gfx::Rect(0, 0, 200, 200); 32 params.parent = window.get(); 33 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 34 params.native_widget = new DesktopNativeWidgetAura(&widget); 35 widget.Init(params); 36 } 37 38 // Verifies that the Aura windows making up a widget instance have the correct 39 // bounds after the widget is resized. 40 TEST_F(DesktopNativeWidgetAuraTest, DesktopAuraWindowSizeTest) { 41 Widget widget; 42 43 // On Linux we test this with popup windows because the WM may ignore the size 44 // suggestion for normal windows. 45 #if defined(OS_LINUX) 46 Widget::InitParams init_params = 47 CreateParams(Widget::InitParams::TYPE_POPUP); 48 #else 49 Widget::InitParams init_params = 50 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 51 #endif 52 53 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 54 init_params.native_widget = new DesktopNativeWidgetAura(&widget); 55 widget.Init(init_params); 56 57 gfx::Rect bounds(0, 0, 100, 100); 58 widget.SetBounds(bounds); 59 widget.Show(); 60 61 EXPECT_EQ(bounds.ToString(), 62 widget.GetNativeView()->GetRootWindow()->bounds().ToString()); 63 EXPECT_EQ(bounds.ToString(), widget.GetNativeView()->bounds().ToString()); 64 EXPECT_EQ(bounds.ToString(), 65 widget.GetNativeView()->parent()->bounds().ToString()); 66 67 gfx::Rect new_bounds(0, 0, 200, 200); 68 widget.SetBounds(new_bounds); 69 EXPECT_EQ(new_bounds.ToString(), 70 widget.GetNativeView()->GetRootWindow()->bounds().ToString()); 71 EXPECT_EQ(new_bounds.ToString(), widget.GetNativeView()->bounds().ToString()); 72 EXPECT_EQ(new_bounds.ToString(), 73 widget.GetNativeView()->parent()->bounds().ToString()); 74 } 75 76 // Verifies GetNativeView() is initially hidden. If the native view is initially 77 // shown then animations can not be disabled. 78 TEST_F(DesktopNativeWidgetAuraTest, NativeViewInitiallyHidden) { 79 Widget widget; 80 Widget::InitParams init_params = 81 CreateParams(Widget::InitParams::TYPE_WINDOW); 82 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 83 init_params.native_widget = new DesktopNativeWidgetAura(&widget); 84 widget.Init(init_params); 85 EXPECT_FALSE(widget.GetNativeView()->IsVisible()); 86 } 87 88 // Verify that the cursor state is shared between two native widgets. 89 TEST_F(DesktopNativeWidgetAuraTest, GlobalCursorState) { 90 // Create two native widgets, each owning different root windows. 91 Widget widget_a; 92 Widget::InitParams init_params_a = 93 CreateParams(Widget::InitParams::TYPE_WINDOW); 94 init_params_a.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 95 DesktopNativeWidgetAura* desktop_native_widget_aura_a = 96 new DesktopNativeWidgetAura(&widget_a); 97 init_params_a.native_widget = desktop_native_widget_aura_a; 98 widget_a.Init(init_params_a); 99 100 Widget widget_b; 101 Widget::InitParams init_params_b = 102 CreateParams(Widget::InitParams::TYPE_WINDOW); 103 init_params_b.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 104 DesktopNativeWidgetAura* desktop_native_widget_aura_b = 105 new DesktopNativeWidgetAura(&widget_b); 106 init_params_b.native_widget = desktop_native_widget_aura_b; 107 widget_b.Init(init_params_b); 108 109 aura::client::CursorClient* cursor_client_a = aura::client::GetCursorClient( 110 desktop_native_widget_aura_a->host()->window()); 111 aura::client::CursorClient* cursor_client_b = aura::client::GetCursorClient( 112 desktop_native_widget_aura_b->host()->window()); 113 114 // Verify the cursor can be locked using one client and unlocked using 115 // another. 116 EXPECT_FALSE(cursor_client_a->IsCursorLocked()); 117 EXPECT_FALSE(cursor_client_b->IsCursorLocked()); 118 119 cursor_client_a->LockCursor(); 120 EXPECT_TRUE(cursor_client_a->IsCursorLocked()); 121 EXPECT_TRUE(cursor_client_b->IsCursorLocked()); 122 123 cursor_client_b->UnlockCursor(); 124 EXPECT_FALSE(cursor_client_a->IsCursorLocked()); 125 EXPECT_FALSE(cursor_client_b->IsCursorLocked()); 126 127 // Verify that mouse events can be disabled using one client and then 128 // re-enabled using another. Note that disabling mouse events should also 129 // have the side effect of making the cursor invisible. 130 EXPECT_TRUE(cursor_client_a->IsCursorVisible()); 131 EXPECT_TRUE(cursor_client_b->IsCursorVisible()); 132 EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled()); 133 EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled()); 134 135 cursor_client_b->DisableMouseEvents(); 136 EXPECT_FALSE(cursor_client_a->IsCursorVisible()); 137 EXPECT_FALSE(cursor_client_b->IsCursorVisible()); 138 EXPECT_FALSE(cursor_client_a->IsMouseEventsEnabled()); 139 EXPECT_FALSE(cursor_client_b->IsMouseEventsEnabled()); 140 141 cursor_client_a->EnableMouseEvents(); 142 EXPECT_TRUE(cursor_client_a->IsCursorVisible()); 143 EXPECT_TRUE(cursor_client_b->IsCursorVisible()); 144 EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled()); 145 EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled()); 146 147 // Verify that setting the cursor using one cursor client 148 // will set it for all root windows. 149 EXPECT_EQ(ui::kCursorNone, cursor_client_a->GetCursor().native_type()); 150 EXPECT_EQ(ui::kCursorNone, cursor_client_b->GetCursor().native_type()); 151 152 cursor_client_b->SetCursor(ui::kCursorPointer); 153 EXPECT_EQ(ui::kCursorPointer, cursor_client_a->GetCursor().native_type()); 154 EXPECT_EQ(ui::kCursorPointer, cursor_client_b->GetCursor().native_type()); 155 156 // Verify that hiding the cursor using one cursor client will 157 // hide it for all root windows. Note that hiding the cursor 158 // should not disable mouse events. 159 cursor_client_a->HideCursor(); 160 EXPECT_FALSE(cursor_client_a->IsCursorVisible()); 161 EXPECT_FALSE(cursor_client_b->IsCursorVisible()); 162 EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled()); 163 EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled()); 164 165 // Verify that the visibility state cannot be changed using one 166 // cursor client when the cursor was locked using another. 167 cursor_client_b->LockCursor(); 168 cursor_client_a->ShowCursor(); 169 EXPECT_FALSE(cursor_client_a->IsCursorVisible()); 170 EXPECT_FALSE(cursor_client_b->IsCursorVisible()); 171 172 // Verify the cursor becomes visible on unlock (since a request 173 // to make it visible was queued up while the cursor was locked). 174 cursor_client_b->UnlockCursor(); 175 EXPECT_TRUE(cursor_client_a->IsCursorVisible()); 176 EXPECT_TRUE(cursor_client_b->IsCursorVisible()); 177 } 178 179 // Verifies FocusController doesn't attempt to access |content_window_| during 180 // destruction. Previously the FocusController was destroyed after the window. 181 // This could be problematic as FocusController references |content_window_| and 182 // could attempt to use it after |content_window_| was destroyed. This test 183 // verifies this doesn't happen. Note that this test only failed under ASAN. 184 TEST_F(DesktopNativeWidgetAuraTest, DontAccessContentWindowDuringDestruction) { 185 aura::test::TestWindowDelegate delegate; 186 { 187 Widget widget; 188 Widget::InitParams init_params = 189 CreateParams(Widget::InitParams::TYPE_WINDOW); 190 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 191 DesktopNativeWidgetAura* desktop_native_widget_aura = 192 new DesktopNativeWidgetAura(&widget); 193 init_params.native_widget = desktop_native_widget_aura; 194 widget.Init(init_params); 195 196 // Owned by |widget|. 197 aura::Window* window = new aura::Window(&delegate); 198 window->Show(); 199 widget.GetNativeWindow()->parent()->AddChild(window); 200 201 widget.Show(); 202 } 203 } 204 205 void QuitNestedLoopAndCloseWidget(scoped_ptr<Widget> widget, 206 base::Closure* quit_runloop) { 207 quit_runloop->Run(); 208 } 209 210 // Verifies that a widget can be destroyed when running a nested message-loop. 211 TEST_F(DesktopNativeWidgetAuraTest, WidgetCanBeDestroyedFromNestedLoop) { 212 scoped_ptr<Widget> widget(new Widget); 213 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); 214 params.bounds = gfx::Rect(0, 0, 200, 200); 215 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 216 params.native_widget = new DesktopNativeWidgetAura(widget.get()); 217 widget->Init(params); 218 widget->Show(); 219 220 aura::Window* window = widget->GetNativeView(); 221 aura::Window* root = window->GetRootWindow(); 222 aura::client::DispatcherClient* client = 223 aura::client::GetDispatcherClient(root); 224 225 // Post a task that terminates the nested loop and destroyes the widget. This 226 // task will be executed from the nested loop initiated with the call to 227 // |RunWithDispatcher()| below. 228 aura::client::DispatcherRunLoop run_loop(client, NULL); 229 base::Closure quit_runloop = run_loop.QuitClosure(); 230 message_loop()->PostTask(FROM_HERE, 231 base::Bind(&QuitNestedLoopAndCloseWidget, 232 base::Passed(&widget), 233 base::Unretained(&quit_runloop))); 234 run_loop.Run(); 235 } 236 237 // This class provides functionality to create fullscreen and top level popup 238 // windows. It additionally tests whether the destruction of these windows 239 // occurs correctly in desktop AURA without crashing. 240 // It provides facilities to test the following cases:- 241 // 1. Child window destroyed which should lead to the destruction of the 242 // parent. 243 // 2. Parent window destroyed which should lead to the child being destroyed. 244 class DesktopAuraTopLevelWindowTest 245 : public views::TestViewsDelegate, 246 public aura::WindowObserver { 247 public: 248 DesktopAuraTopLevelWindowTest() 249 : top_level_widget_(NULL), 250 owned_window_(NULL), 251 owner_destroyed_(false), 252 owned_window_destroyed_(false) {} 253 254 virtual ~DesktopAuraTopLevelWindowTest() { 255 EXPECT_TRUE(owner_destroyed_); 256 EXPECT_TRUE(owned_window_destroyed_); 257 top_level_widget_ = NULL; 258 owned_window_ = NULL; 259 } 260 261 // views::TestViewsDelegate overrides. 262 virtual void OnBeforeWidgetInit( 263 Widget::InitParams* params, 264 internal::NativeWidgetDelegate* delegate) OVERRIDE { 265 if (!params->native_widget) 266 params->native_widget = new views::DesktopNativeWidgetAura(delegate); 267 } 268 269 void CreateTopLevelWindow(const gfx::Rect& bounds, bool fullscreen) { 270 Widget::InitParams init_params; 271 init_params.type = Widget::InitParams::TYPE_WINDOW; 272 init_params.bounds = bounds; 273 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 274 init_params.layer_type = aura::WINDOW_LAYER_NOT_DRAWN; 275 init_params.accept_events = fullscreen; 276 277 widget_.Init(init_params); 278 279 owned_window_ = new aura::Window(&child_window_delegate_); 280 owned_window_->SetType(ui::wm::WINDOW_TYPE_NORMAL); 281 owned_window_->SetName("TestTopLevelWindow"); 282 if (fullscreen) { 283 owned_window_->SetProperty(aura::client::kShowStateKey, 284 ui::SHOW_STATE_FULLSCREEN); 285 } else { 286 owned_window_->SetType(ui::wm::WINDOW_TYPE_MENU); 287 } 288 owned_window_->Init(aura::WINDOW_LAYER_TEXTURED); 289 aura::client::ParentWindowWithContext( 290 owned_window_, 291 widget_.GetNativeView()->GetRootWindow(), 292 gfx::Rect(0, 0, 1900, 1600)); 293 owned_window_->Show(); 294 owned_window_->AddObserver(this); 295 296 ASSERT_TRUE(owned_window_->parent() != NULL); 297 owned_window_->parent()->AddObserver(this); 298 299 top_level_widget_ = 300 views::Widget::GetWidgetForNativeView(owned_window_->parent()); 301 ASSERT_TRUE(top_level_widget_ != NULL); 302 } 303 304 void DestroyOwnedWindow() { 305 ASSERT_TRUE(owned_window_ != NULL); 306 delete owned_window_; 307 } 308 309 void DestroyOwnerWindow() { 310 ASSERT_TRUE(top_level_widget_ != NULL); 311 top_level_widget_->CloseNow(); 312 } 313 314 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { 315 window->RemoveObserver(this); 316 if (window == owned_window_) { 317 owned_window_destroyed_ = true; 318 } else if (window == top_level_widget_->GetNativeView()) { 319 owner_destroyed_ = true; 320 } else { 321 ADD_FAILURE() << "Unexpected window destroyed callback: " << window; 322 } 323 } 324 325 aura::Window* owned_window() { 326 return owned_window_; 327 } 328 329 views::Widget* top_level_widget() { 330 return top_level_widget_; 331 } 332 333 private: 334 views::Widget widget_; 335 views::Widget* top_level_widget_; 336 aura::Window* owned_window_; 337 bool owner_destroyed_; 338 bool owned_window_destroyed_; 339 aura::test::TestWindowDelegate child_window_delegate_; 340 341 DISALLOW_COPY_AND_ASSIGN(DesktopAuraTopLevelWindowTest); 342 }; 343 344 typedef WidgetTest DesktopAuraWidgetTest; 345 346 TEST_F(DesktopAuraWidgetTest, FullscreenWindowDestroyedBeforeOwnerTest) { 347 ViewsDelegate::views_delegate = NULL; 348 DesktopAuraTopLevelWindowTest fullscreen_window; 349 ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow( 350 gfx::Rect(0, 0, 200, 200), true)); 351 352 RunPendingMessages(); 353 ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnedWindow()); 354 RunPendingMessages(); 355 } 356 357 TEST_F(DesktopAuraWidgetTest, FullscreenWindowOwnerDestroyed) { 358 ViewsDelegate::views_delegate = NULL; 359 360 DesktopAuraTopLevelWindowTest fullscreen_window; 361 ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow( 362 gfx::Rect(0, 0, 200, 200), true)); 363 364 RunPendingMessages(); 365 ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnerWindow()); 366 RunPendingMessages(); 367 } 368 369 TEST_F(DesktopAuraWidgetTest, TopLevelOwnedPopupTest) { 370 ViewsDelegate::views_delegate = NULL; 371 DesktopAuraTopLevelWindowTest popup_window; 372 ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow( 373 gfx::Rect(0, 0, 200, 200), false)); 374 375 RunPendingMessages(); 376 ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow()); 377 RunPendingMessages(); 378 } 379 380 // This test validates that when a top level owned popup Aura window is 381 // resized, the widget is resized as well. 382 TEST_F(DesktopAuraWidgetTest, TopLevelOwnedPopupResizeTest) { 383 ViewsDelegate::views_delegate = NULL; 384 DesktopAuraTopLevelWindowTest popup_window; 385 ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow( 386 gfx::Rect(0, 0, 200, 200), false)); 387 388 gfx::Rect new_size(0, 0, 400, 400); 389 popup_window.owned_window()->SetBounds(new_size); 390 391 EXPECT_EQ(popup_window.top_level_widget()->GetNativeView()->bounds().size(), 392 new_size.size()); 393 RunPendingMessages(); 394 ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow()); 395 RunPendingMessages(); 396 } 397 398 } // namespace test 399 } // namespace views 400