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/gfx/screen.h"
16 #include "ui/views/test/views_test_base.h"
17 #include "ui/views/test/widget_test.h"
18 #include "ui/views/widget/widget.h"
19 #include "ui/wm/public/dispatcher_client.h"
24 typedef ViewsTestBase DesktopNativeWidgetAuraTest
;
26 // Verifies creating a Widget with a parent that is not in a RootWindow doesn't
28 TEST_F(DesktopNativeWidgetAuraTest
, CreateWithParentNotInRootWindow
) {
29 scoped_ptr
<aura::Window
> window(new aura::Window(NULL
));
31 Widget::InitParams params
= CreateParams(Widget::InitParams::TYPE_WINDOW
);
32 params
.bounds
= gfx::Rect(0, 0, 200, 200);
33 params
.parent
= window
.get();
34 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
35 params
.native_widget
= new DesktopNativeWidgetAura(&widget
);
39 // Verifies that the Aura windows making up a widget instance have the correct
40 // bounds after the widget is resized.
41 TEST_F(DesktopNativeWidgetAuraTest
, DesktopAuraWindowSizeTest
) {
44 // On Linux we test this with popup windows because the WM may ignore the size
45 // suggestion for normal windows.
47 Widget::InitParams init_params
=
48 CreateParams(Widget::InitParams::TYPE_POPUP
);
50 Widget::InitParams init_params
=
51 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
54 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
55 init_params
.native_widget
= new DesktopNativeWidgetAura(&widget
);
56 widget
.Init(init_params
);
58 gfx::Rect
bounds(0, 0, 100, 100);
59 widget
.SetBounds(bounds
);
62 EXPECT_EQ(bounds
.ToString(),
63 widget
.GetNativeView()->GetRootWindow()->bounds().ToString());
64 EXPECT_EQ(bounds
.ToString(), widget
.GetNativeView()->bounds().ToString());
65 EXPECT_EQ(bounds
.ToString(),
66 widget
.GetNativeView()->parent()->bounds().ToString());
68 gfx::Rect
new_bounds(0, 0, 200, 200);
69 widget
.SetBounds(new_bounds
);
70 EXPECT_EQ(new_bounds
.ToString(),
71 widget
.GetNativeView()->GetRootWindow()->bounds().ToString());
72 EXPECT_EQ(new_bounds
.ToString(), widget
.GetNativeView()->bounds().ToString());
73 EXPECT_EQ(new_bounds
.ToString(),
74 widget
.GetNativeView()->parent()->bounds().ToString());
77 // Verifies GetNativeView() is initially hidden. If the native view is initially
78 // shown then animations can not be disabled.
79 TEST_F(DesktopNativeWidgetAuraTest
, NativeViewInitiallyHidden
) {
81 Widget::InitParams init_params
=
82 CreateParams(Widget::InitParams::TYPE_WINDOW
);
83 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
84 init_params
.native_widget
= new DesktopNativeWidgetAura(&widget
);
85 widget
.Init(init_params
);
86 EXPECT_FALSE(widget
.GetNativeView()->IsVisible());
89 // Verify that the cursor state is shared between two native widgets.
90 TEST_F(DesktopNativeWidgetAuraTest
, GlobalCursorState
) {
91 // Create two native widgets, each owning different root windows.
93 Widget::InitParams init_params_a
=
94 CreateParams(Widget::InitParams::TYPE_WINDOW
);
95 init_params_a
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
96 DesktopNativeWidgetAura
* desktop_native_widget_aura_a
=
97 new DesktopNativeWidgetAura(&widget_a
);
98 init_params_a
.native_widget
= desktop_native_widget_aura_a
;
99 widget_a
.Init(init_params_a
);
102 Widget::InitParams init_params_b
=
103 CreateParams(Widget::InitParams::TYPE_WINDOW
);
104 init_params_b
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
105 DesktopNativeWidgetAura
* desktop_native_widget_aura_b
=
106 new DesktopNativeWidgetAura(&widget_b
);
107 init_params_b
.native_widget
= desktop_native_widget_aura_b
;
108 widget_b
.Init(init_params_b
);
110 aura::client::CursorClient
* cursor_client_a
= aura::client::GetCursorClient(
111 desktop_native_widget_aura_a
->host()->window());
112 aura::client::CursorClient
* cursor_client_b
= aura::client::GetCursorClient(
113 desktop_native_widget_aura_b
->host()->window());
115 // Verify the cursor can be locked using one client and unlocked using
117 EXPECT_FALSE(cursor_client_a
->IsCursorLocked());
118 EXPECT_FALSE(cursor_client_b
->IsCursorLocked());
120 cursor_client_a
->LockCursor();
121 EXPECT_TRUE(cursor_client_a
->IsCursorLocked());
122 EXPECT_TRUE(cursor_client_b
->IsCursorLocked());
124 cursor_client_b
->UnlockCursor();
125 EXPECT_FALSE(cursor_client_a
->IsCursorLocked());
126 EXPECT_FALSE(cursor_client_b
->IsCursorLocked());
128 // Verify that mouse events can be disabled using one client and then
129 // re-enabled using another. Note that disabling mouse events should also
130 // have the side effect of making the cursor invisible.
131 EXPECT_TRUE(cursor_client_a
->IsCursorVisible());
132 EXPECT_TRUE(cursor_client_b
->IsCursorVisible());
133 EXPECT_TRUE(cursor_client_a
->IsMouseEventsEnabled());
134 EXPECT_TRUE(cursor_client_b
->IsMouseEventsEnabled());
136 cursor_client_b
->DisableMouseEvents();
137 EXPECT_FALSE(cursor_client_a
->IsCursorVisible());
138 EXPECT_FALSE(cursor_client_b
->IsCursorVisible());
139 EXPECT_FALSE(cursor_client_a
->IsMouseEventsEnabled());
140 EXPECT_FALSE(cursor_client_b
->IsMouseEventsEnabled());
142 cursor_client_a
->EnableMouseEvents();
143 EXPECT_TRUE(cursor_client_a
->IsCursorVisible());
144 EXPECT_TRUE(cursor_client_b
->IsCursorVisible());
145 EXPECT_TRUE(cursor_client_a
->IsMouseEventsEnabled());
146 EXPECT_TRUE(cursor_client_b
->IsMouseEventsEnabled());
148 // Verify that setting the cursor using one cursor client
149 // will set it for all root windows.
150 EXPECT_EQ(ui::kCursorNone
, cursor_client_a
->GetCursor().native_type());
151 EXPECT_EQ(ui::kCursorNone
, cursor_client_b
->GetCursor().native_type());
153 cursor_client_b
->SetCursor(ui::kCursorPointer
);
154 EXPECT_EQ(ui::kCursorPointer
, cursor_client_a
->GetCursor().native_type());
155 EXPECT_EQ(ui::kCursorPointer
, cursor_client_b
->GetCursor().native_type());
157 // Verify that hiding the cursor using one cursor client will
158 // hide it for all root windows. Note that hiding the cursor
159 // should not disable mouse events.
160 cursor_client_a
->HideCursor();
161 EXPECT_FALSE(cursor_client_a
->IsCursorVisible());
162 EXPECT_FALSE(cursor_client_b
->IsCursorVisible());
163 EXPECT_TRUE(cursor_client_a
->IsMouseEventsEnabled());
164 EXPECT_TRUE(cursor_client_b
->IsMouseEventsEnabled());
166 // Verify that the visibility state cannot be changed using one
167 // cursor client when the cursor was locked using another.
168 cursor_client_b
->LockCursor();
169 cursor_client_a
->ShowCursor();
170 EXPECT_FALSE(cursor_client_a
->IsCursorVisible());
171 EXPECT_FALSE(cursor_client_b
->IsCursorVisible());
173 // Verify the cursor becomes visible on unlock (since a request
174 // to make it visible was queued up while the cursor was locked).
175 cursor_client_b
->UnlockCursor();
176 EXPECT_TRUE(cursor_client_a
->IsCursorVisible());
177 EXPECT_TRUE(cursor_client_b
->IsCursorVisible());
180 // Verifies FocusController doesn't attempt to access |content_window_| during
181 // destruction. Previously the FocusController was destroyed after the window.
182 // This could be problematic as FocusController references |content_window_| and
183 // could attempt to use it after |content_window_| was destroyed. This test
184 // verifies this doesn't happen. Note that this test only failed under ASAN.
185 TEST_F(DesktopNativeWidgetAuraTest
, DontAccessContentWindowDuringDestruction
) {
186 aura::test::TestWindowDelegate delegate
;
189 Widget::InitParams init_params
=
190 CreateParams(Widget::InitParams::TYPE_WINDOW
);
191 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
192 DesktopNativeWidgetAura
* desktop_native_widget_aura
=
193 new DesktopNativeWidgetAura(&widget
);
194 init_params
.native_widget
= desktop_native_widget_aura
;
195 widget
.Init(init_params
);
197 // Owned by |widget|.
198 aura::Window
* window
= new aura::Window(&delegate
);
200 widget
.GetNativeWindow()->parent()->AddChild(window
);
206 void QuitNestedLoopAndCloseWidget(scoped_ptr
<Widget
> widget
,
207 base::Closure
* quit_runloop
) {
211 // Verifies that a widget can be destroyed when running a nested message-loop.
212 TEST_F(DesktopNativeWidgetAuraTest
, WidgetCanBeDestroyedFromNestedLoop
) {
213 scoped_ptr
<Widget
> widget(new Widget
);
214 Widget::InitParams params
= CreateParams(Widget::InitParams::TYPE_WINDOW
);
215 params
.bounds
= gfx::Rect(0, 0, 200, 200);
216 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
217 params
.native_widget
= new DesktopNativeWidgetAura(widget
.get());
218 widget
->Init(params
);
221 aura::Window
* window
= widget
->GetNativeView();
222 aura::Window
* root
= window
->GetRootWindow();
223 aura::client::DispatcherClient
* client
=
224 aura::client::GetDispatcherClient(root
);
226 // Post a task that terminates the nested loop and destroyes the widget. This
227 // task will be executed from the nested loop initiated with the call to
228 // |RunWithDispatcher()| below.
229 aura::client::DispatcherRunLoop
run_loop(client
, NULL
);
230 base::Closure quit_runloop
= run_loop
.QuitClosure();
231 message_loop()->PostTask(FROM_HERE
,
232 base::Bind(&QuitNestedLoopAndCloseWidget
,
233 base::Passed(&widget
),
234 base::Unretained(&quit_runloop
)));
238 // This class provides functionality to create fullscreen and top level popup
239 // windows. It additionally tests whether the destruction of these windows
240 // occurs correctly in desktop AURA without crashing.
241 // It provides facilities to test the following cases:-
242 // 1. Child window destroyed which should lead to the destruction of the
244 // 2. Parent window destroyed which should lead to the child being destroyed.
245 class DesktopAuraTopLevelWindowTest
246 : public views::TestViewsDelegate
,
247 public aura::WindowObserver
{
249 DesktopAuraTopLevelWindowTest()
250 : top_level_widget_(NULL
),
252 owner_destroyed_(false),
253 owned_window_destroyed_(false),
254 use_async_mode_(true) {}
256 ~DesktopAuraTopLevelWindowTest() override
{
257 EXPECT_TRUE(owner_destroyed_
);
258 EXPECT_TRUE(owned_window_destroyed_
);
259 top_level_widget_
= NULL
;
260 owned_window_
= NULL
;
263 // views::TestViewsDelegate overrides.
264 void OnBeforeWidgetInit(Widget::InitParams
* params
,
265 internal::NativeWidgetDelegate
* delegate
) override
{
266 if (!params
->native_widget
)
267 params
->native_widget
= new views::DesktopNativeWidgetAura(delegate
);
270 void CreateTopLevelWindow(const gfx::Rect
& bounds
, bool fullscreen
) {
271 Widget::InitParams init_params
;
272 init_params
.type
= Widget::InitParams::TYPE_WINDOW
;
273 init_params
.bounds
= bounds
;
274 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
275 init_params
.layer_type
= aura::WINDOW_LAYER_NOT_DRAWN
;
276 init_params
.accept_events
= fullscreen
;
278 widget_
.Init(init_params
);
280 owned_window_
= new aura::Window(&child_window_delegate_
);
281 owned_window_
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
282 owned_window_
->SetName("TestTopLevelWindow");
284 owned_window_
->SetProperty(aura::client::kShowStateKey
,
285 ui::SHOW_STATE_FULLSCREEN
);
287 owned_window_
->SetType(ui::wm::WINDOW_TYPE_MENU
);
289 owned_window_
->Init(aura::WINDOW_LAYER_TEXTURED
);
290 aura::client::ParentWindowWithContext(
292 widget_
.GetNativeView()->GetRootWindow(),
293 gfx::Rect(0, 0, 1900, 1600));
294 owned_window_
->Show();
295 owned_window_
->AddObserver(this);
297 ASSERT_TRUE(owned_window_
->parent() != NULL
);
298 owned_window_
->parent()->AddObserver(this);
301 views::Widget::GetWidgetForNativeView(owned_window_
->parent());
302 ASSERT_TRUE(top_level_widget_
!= NULL
);
305 void DestroyOwnedWindow() {
306 ASSERT_TRUE(owned_window_
!= NULL
);
307 // If async mode is off then clean up state here.
308 if (!use_async_mode_
) {
309 owned_window_
->RemoveObserver(this);
310 owned_window_
->parent()->RemoveObserver(this);
311 owner_destroyed_
= true;
312 owned_window_destroyed_
= true;
314 delete owned_window_
;
317 void DestroyOwnerWindow() {
318 ASSERT_TRUE(top_level_widget_
!= NULL
);
319 top_level_widget_
->CloseNow();
322 void OnWindowDestroying(aura::Window
* window
) override
{
323 window
->RemoveObserver(this);
324 if (window
== owned_window_
) {
325 owned_window_destroyed_
= true;
326 } else if (window
== top_level_widget_
->GetNativeView()) {
327 owner_destroyed_
= true;
329 ADD_FAILURE() << "Unexpected window destroyed callback: " << window
;
333 aura::Window
* owned_window() {
334 return owned_window_
;
337 views::Widget
* top_level_widget() {
338 return top_level_widget_
;
341 void set_use_async_mode(bool async_mode
) {
342 use_async_mode_
= async_mode
;
346 views::Widget widget_
;
347 views::Widget
* top_level_widget_
;
348 aura::Window
* owned_window_
;
349 bool owner_destroyed_
;
350 bool owned_window_destroyed_
;
351 aura::test::TestWindowDelegate child_window_delegate_
;
352 // This flag controls whether we need to wait for the destruction to complete
353 // before finishing the test. Defaults to true.
354 bool use_async_mode_
;
356 DISALLOW_COPY_AND_ASSIGN(DesktopAuraTopLevelWindowTest
);
359 typedef WidgetTest DesktopAuraWidgetTest
;
361 TEST_F(DesktopAuraWidgetTest
, FullscreenWindowDestroyedBeforeOwnerTest
) {
362 ViewsDelegate::views_delegate
= NULL
;
363 DesktopAuraTopLevelWindowTest fullscreen_window
;
364 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.CreateTopLevelWindow(
365 gfx::Rect(0, 0, 200, 200), true));
367 RunPendingMessages();
368 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.DestroyOwnedWindow());
369 RunPendingMessages();
372 TEST_F(DesktopAuraWidgetTest
, FullscreenWindowOwnerDestroyed
) {
373 ViewsDelegate::views_delegate
= NULL
;
375 DesktopAuraTopLevelWindowTest fullscreen_window
;
376 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.CreateTopLevelWindow(
377 gfx::Rect(0, 0, 200, 200), true));
379 RunPendingMessages();
380 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.DestroyOwnerWindow());
381 RunPendingMessages();
384 TEST_F(DesktopAuraWidgetTest
, TopLevelOwnedPopupTest
) {
385 ViewsDelegate::views_delegate
= NULL
;
386 DesktopAuraTopLevelWindowTest popup_window
;
387 ASSERT_NO_FATAL_FAILURE(popup_window
.CreateTopLevelWindow(
388 gfx::Rect(0, 0, 200, 200), false));
390 RunPendingMessages();
391 ASSERT_NO_FATAL_FAILURE(popup_window
.DestroyOwnedWindow());
392 RunPendingMessages();
395 // This test validates that when a top level owned popup Aura window is
396 // resized, the widget is resized as well.
397 TEST_F(DesktopAuraWidgetTest
, TopLevelOwnedPopupResizeTest
) {
398 ViewsDelegate::views_delegate
= NULL
;
399 DesktopAuraTopLevelWindowTest popup_window
;
401 popup_window
.set_use_async_mode(false);
403 ASSERT_NO_FATAL_FAILURE(popup_window
.CreateTopLevelWindow(
404 gfx::Rect(0, 0, 200, 200), false));
406 gfx::Rect
new_size(0, 0, 400, 400);
407 popup_window
.owned_window()->SetBounds(new_size
);
409 EXPECT_EQ(popup_window
.top_level_widget()->GetNativeView()->bounds().size(),
412 ASSERT_NO_FATAL_FAILURE(popup_window
.DestroyOwnedWindow());
415 // This test validates that when a top level owned popup Aura window is
416 // repositioned, the widget is repositioned as well.
417 TEST_F(DesktopAuraWidgetTest
, TopLevelOwnedPopupRepositionTest
) {
418 ViewsDelegate::views_delegate
= NULL
;
419 DesktopAuraTopLevelWindowTest popup_window
;
421 popup_window
.set_use_async_mode(false);
423 ASSERT_NO_FATAL_FAILURE(popup_window
.CreateTopLevelWindow(
424 gfx::Rect(0, 0, 200, 200), false));
426 gfx::Rect
new_pos(10, 10, 400, 400);
427 popup_window
.owned_window()->SetBoundsInScreen(
429 gfx::Screen::GetScreenFor(
430 popup_window
.owned_window())->GetDisplayNearestPoint(gfx::Point()));
433 popup_window
.top_level_widget()->GetWindowBoundsInScreen());
435 ASSERT_NO_FATAL_FAILURE(popup_window
.DestroyOwnedWindow());