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.
5 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.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/test_window_delegate.h"
12 #include "ui/aura/window.h"
13 #include "ui/aura/window_tree_host.h"
14 #include "ui/events/test/event_generator.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"
23 typedef ViewsTestBase DesktopNativeWidgetAuraTest
;
25 // Verifies creating a Widget with a parent that is not in a RootWindow doesn't
27 TEST_F(DesktopNativeWidgetAuraTest
, CreateWithParentNotInRootWindow
) {
28 scoped_ptr
<aura::Window
> window(new aura::Window(NULL
));
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
);
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
) {
43 // On Linux we test this with popup windows because the WM may ignore the size
44 // suggestion for normal windows.
46 Widget::InitParams init_params
=
47 CreateParams(Widget::InitParams::TYPE_POPUP
);
49 Widget::InitParams init_params
=
50 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
53 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
54 init_params
.native_widget
= new DesktopNativeWidgetAura(&widget
);
55 widget
.Init(init_params
);
57 gfx::Rect
bounds(0, 0, 100, 100);
58 widget
.SetBounds(bounds
);
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());
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());
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
) {
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());
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.
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
);
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
);
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());
114 // Verify the cursor can be locked using one client and unlocked using
116 EXPECT_FALSE(cursor_client_a
->IsCursorLocked());
117 EXPECT_FALSE(cursor_client_b
->IsCursorLocked());
119 cursor_client_a
->LockCursor();
120 EXPECT_TRUE(cursor_client_a
->IsCursorLocked());
121 EXPECT_TRUE(cursor_client_b
->IsCursorLocked());
123 cursor_client_b
->UnlockCursor();
124 EXPECT_FALSE(cursor_client_a
->IsCursorLocked());
125 EXPECT_FALSE(cursor_client_b
->IsCursorLocked());
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());
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());
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());
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());
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());
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());
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());
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());
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
;
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
);
196 // Owned by |widget|.
197 aura::Window
* window
= new aura::Window(&delegate
);
199 widget
.GetNativeWindow()->parent()->AddChild(window
);
205 void QuitNestedLoopAndCloseWidget(scoped_ptr
<Widget
> widget
,
206 base::Closure
* quit_runloop
) {
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
);
220 aura::Window
* window
= widget
->GetNativeView();
221 aura::Window
* root
= window
->GetRootWindow();
222 aura::client::DispatcherClient
* client
=
223 aura::client::GetDispatcherClient(root
);
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
)));
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
243 // 2. Parent window destroyed which should lead to the child being destroyed.
244 class DesktopAuraTopLevelWindowTest
245 : public views::TestViewsDelegate
,
246 public aura::WindowObserver
{
248 DesktopAuraTopLevelWindowTest()
249 : top_level_widget_(NULL
),
251 owner_destroyed_(false),
252 owned_window_destroyed_(false) {}
254 virtual ~DesktopAuraTopLevelWindowTest() {
255 EXPECT_TRUE(owner_destroyed_
);
256 EXPECT_TRUE(owned_window_destroyed_
);
257 top_level_widget_
= NULL
;
258 owned_window_
= NULL
;
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
);
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
;
277 widget_
.Init(init_params
);
279 owned_window_
= new aura::Window(&child_window_delegate_
);
280 owned_window_
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
281 owned_window_
->SetName("TestTopLevelWindow");
283 owned_window_
->SetProperty(aura::client::kShowStateKey
,
284 ui::SHOW_STATE_FULLSCREEN
);
286 owned_window_
->SetType(ui::wm::WINDOW_TYPE_MENU
);
288 owned_window_
->Init(aura::WINDOW_LAYER_TEXTURED
);
289 aura::client::ParentWindowWithContext(
291 widget_
.GetNativeView()->GetRootWindow(),
292 gfx::Rect(0, 0, 1900, 1600));
293 owned_window_
->Show();
294 owned_window_
->AddObserver(this);
296 ASSERT_TRUE(owned_window_
->parent() != NULL
);
297 owned_window_
->parent()->AddObserver(this);
300 views::Widget::GetWidgetForNativeView(owned_window_
->parent());
301 ASSERT_TRUE(top_level_widget_
!= NULL
);
304 void DestroyOwnedWindow() {
305 ASSERT_TRUE(owned_window_
!= NULL
);
306 delete owned_window_
;
309 void DestroyOwnerWindow() {
310 ASSERT_TRUE(top_level_widget_
!= NULL
);
311 top_level_widget_
->CloseNow();
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;
321 ADD_FAILURE() << "Unexpected window destroyed callback: " << window
;
325 aura::Window
* owned_window() {
326 return owned_window_
;
329 views::Widget
* top_level_widget() {
330 return top_level_widget_
;
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_
;
341 DISALLOW_COPY_AND_ASSIGN(DesktopAuraTopLevelWindowTest
);
344 typedef WidgetTest DesktopAuraWidgetTest
;
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));
352 RunPendingMessages();
353 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.DestroyOwnedWindow());
354 RunPendingMessages();
357 TEST_F(DesktopAuraWidgetTest
, FullscreenWindowOwnerDestroyed
) {
358 ViewsDelegate::views_delegate
= NULL
;
360 DesktopAuraTopLevelWindowTest fullscreen_window
;
361 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.CreateTopLevelWindow(
362 gfx::Rect(0, 0, 200, 200), true));
364 RunPendingMessages();
365 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.DestroyOwnerWindow());
366 RunPendingMessages();
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));
375 RunPendingMessages();
376 ASSERT_NO_FATAL_FAILURE(popup_window
.DestroyOwnedWindow());
377 RunPendingMessages();
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));
388 gfx::Rect
new_size(0, 0, 400, 400);
389 popup_window
.owned_window()->SetBounds(new_size
);
391 EXPECT_EQ(popup_window
.top_level_widget()->GetNativeView()->bounds().size(),
393 RunPendingMessages();
394 ASSERT_NO_FATAL_FAILURE(popup_window
.DestroyOwnedWindow());
395 RunPendingMessages();