1 // Copyright 2014 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/wm/core/transient_window_manager.h"
7 #include "ui/aura/client/window_tree_client.h"
8 #include "ui/aura/test/aura_test_base.h"
9 #include "ui/aura/test/test_windows.h"
10 #include "ui/aura/window.h"
11 #include "ui/wm/core/transient_window_observer.h"
12 #include "ui/wm/core/window_util.h"
13 #include "ui/wm/core/wm_state.h"
17 using aura::test::ChildWindowIDsAsString
;
18 using aura::test::CreateTestWindowWithId
;
22 class TestTransientWindowObserver
: public TransientWindowObserver
{
24 TestTransientWindowObserver() : add_count_(0), remove_count_(0) {
27 ~TestTransientWindowObserver() override
{}
29 int add_count() const { return add_count_
; }
30 int remove_count() const { return remove_count_
; }
32 // TransientWindowObserver overrides:
33 void OnTransientChildAdded(Window
* window
, Window
* transient
) override
{
36 void OnTransientChildRemoved(Window
* window
, Window
* transient
) override
{
44 DISALLOW_COPY_AND_ASSIGN(TestTransientWindowObserver
);
47 class TransientWindowManagerTest
: public aura::test::AuraTestBase
{
49 TransientWindowManagerTest() {}
50 ~TransientWindowManagerTest() override
{}
52 void SetUp() override
{
53 AuraTestBase::SetUp();
54 wm_state_
.reset(new wm::WMState
);
57 void TearDown() override
{
59 AuraTestBase::TearDown();
63 // Creates a transient window that is transient to |parent|.
64 Window
* CreateTransientChild(int id
, Window
* parent
) {
65 Window
* window
= new Window(NULL
);
67 window
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
68 window
->Init(ui::LAYER_TEXTURED
);
69 AddTransientChild(parent
, window
);
70 aura::client::ParentWindowWithContext(window
, root_window(), gfx::Rect());
75 scoped_ptr
<wm::WMState
> wm_state_
;
77 DISALLOW_COPY_AND_ASSIGN(TransientWindowManagerTest
);
80 // Various assertions for transient children.
81 TEST_F(TransientWindowManagerTest
, TransientChildren
) {
82 scoped_ptr
<Window
> parent(CreateTestWindowWithId(0, root_window()));
83 scoped_ptr
<Window
> w1(CreateTestWindowWithId(1, parent
.get()));
84 scoped_ptr
<Window
> w3(CreateTestWindowWithId(3, parent
.get()));
85 Window
* w2
= CreateTestWindowWithId(2, parent
.get());
86 // w2 is now owned by w1.
87 AddTransientChild(w1
.get(), w2
);
88 // Stack w1 at the top (end), this should force w2 to be last (on top of w1).
89 parent
->StackChildAtTop(w1
.get());
90 ASSERT_EQ(3u, parent
->children().size());
91 EXPECT_EQ(w2
, parent
->children().back());
93 // Destroy w1, which should also destroy w3 (since it's a transient child).
96 ASSERT_EQ(1u, parent
->children().size());
97 EXPECT_EQ(w3
.get(), parent
->children()[0]);
99 w1
.reset(CreateTestWindowWithId(4, parent
.get()));
100 w2
= CreateTestWindowWithId(5, w3
.get());
101 AddTransientChild(w1
.get(), w2
);
102 parent
->StackChildAtTop(w3
.get());
103 // Stack w1 at the top (end), this shouldn't affect w2 since it has a
105 parent
->StackChildAtTop(w1
.get());
106 ASSERT_EQ(2u, parent
->children().size());
107 EXPECT_EQ(w3
.get(), parent
->children()[0]);
108 EXPECT_EQ(w1
.get(), parent
->children()[1]);
110 // Hiding parent should hide transient children.
111 EXPECT_TRUE(w2
->IsVisible());
113 EXPECT_FALSE(w2
->IsVisible());
115 // And they should stay hidden even after the parent became visible.
117 EXPECT_FALSE(w2
->IsVisible());
119 // Hidden transient child should stay hidden regardless of
120 // parent's visibility.
122 EXPECT_FALSE(w2
->IsVisible());
124 EXPECT_FALSE(w2
->IsVisible());
126 EXPECT_FALSE(w2
->IsVisible());
128 // Transient child can be shown even if the transient parent is hidden.
130 EXPECT_FALSE(w2
->IsVisible());
132 EXPECT_TRUE(w2
->IsVisible());
134 EXPECT_TRUE(w2
->IsVisible());
136 // When the parent_controls_visibility is true, TransientWindowManager
137 // controls the children's visibility. It stays invisible even if
138 // Window::Show() is called, and gets shown when the parent becomes visible.
139 wm::TransientWindowManager::Get(w2
)->set_parent_controls_visibility(true);
141 EXPECT_FALSE(w2
->IsVisible());
143 EXPECT_FALSE(w2
->IsVisible());
145 EXPECT_TRUE(w2
->IsVisible());
147 // Hiding a transient child that is hidden by the transient parent
148 // is not currently handled and will be shown anyway.
150 EXPECT_FALSE(w2
->IsVisible());
152 EXPECT_FALSE(w2
->IsVisible());
154 EXPECT_TRUE(w2
->IsVisible());
157 // Tests that transient children are stacked as a unit when using stack above.
158 TEST_F(TransientWindowManagerTest
, TransientChildrenGroupAbove
) {
159 scoped_ptr
<Window
> parent(CreateTestWindowWithId(0, root_window()));
160 scoped_ptr
<Window
> w1(CreateTestWindowWithId(1, parent
.get()));
161 Window
* w11
= CreateTestWindowWithId(11, parent
.get());
162 scoped_ptr
<Window
> w2(CreateTestWindowWithId(2, parent
.get()));
163 Window
* w21
= CreateTestWindowWithId(21, parent
.get());
164 Window
* w211
= CreateTestWindowWithId(211, parent
.get());
165 Window
* w212
= CreateTestWindowWithId(212, parent
.get());
166 Window
* w213
= CreateTestWindowWithId(213, parent
.get());
167 Window
* w22
= CreateTestWindowWithId(22, parent
.get());
168 ASSERT_EQ(8u, parent
->children().size());
170 // w11 is now owned by w1.
171 AddTransientChild(w1
.get(), w11
);
172 // w21 is now owned by w2.
173 AddTransientChild(w2
.get(), w21
);
174 // w22 is now owned by w2.
175 AddTransientChild(w2
.get(), w22
);
176 // w211 is now owned by w21.
177 AddTransientChild(w21
, w211
);
178 // w212 is now owned by w21.
179 AddTransientChild(w21
, w212
);
180 // w213 is now owned by w21.
181 AddTransientChild(w21
, w213
);
182 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent
.get()));
184 // Stack w1 at the top (end), this should force w11 to be last (on top of w1).
185 parent
->StackChildAtTop(w1
.get());
186 EXPECT_EQ(w11
, parent
->children().back());
187 EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent
.get()));
189 // This tests that the order in children_ array rather than in
190 // transient_children_ array is used when reinserting transient children.
191 // If transient_children_ array was used '22' would be following '21'.
192 parent
->StackChildAtTop(w2
.get());
193 EXPECT_EQ(w22
, parent
->children().back());
194 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent
.get()));
196 parent
->StackChildAbove(w11
, w2
.get());
197 EXPECT_EQ(w11
, parent
->children().back());
198 EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent
.get()));
200 parent
->StackChildAbove(w21
, w1
.get());
201 EXPECT_EQ(w22
, parent
->children().back());
202 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent
.get()));
204 parent
->StackChildAbove(w21
, w22
);
205 EXPECT_EQ(w213
, parent
->children().back());
206 EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent
.get()));
208 parent
->StackChildAbove(w11
, w21
);
209 EXPECT_EQ(w11
, parent
->children().back());
210 EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent
.get()));
212 parent
->StackChildAbove(w213
, w21
);
213 EXPECT_EQ(w11
, parent
->children().back());
214 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent
.get()));
216 // No change when stacking a transient parent above its transient child.
217 parent
->StackChildAbove(w21
, w211
);
218 EXPECT_EQ(w11
, parent
->children().back());
219 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent
.get()));
221 // This tests that the order in children_ array rather than in
222 // transient_children_ array is used when reinserting transient children.
223 // If transient_children_ array was used '22' would be following '21'.
224 parent
->StackChildAbove(w2
.get(), w1
.get());
225 EXPECT_EQ(w212
, parent
->children().back());
226 EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent
.get()));
228 parent
->StackChildAbove(w11
, w213
);
229 EXPECT_EQ(w11
, parent
->children().back());
230 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent
.get()));
233 // Tests that transient children are stacked as a unit when using stack below.
234 TEST_F(TransientWindowManagerTest
, TransientChildrenGroupBelow
) {
235 scoped_ptr
<Window
> parent(CreateTestWindowWithId(0, root_window()));
236 scoped_ptr
<Window
> w1(CreateTestWindowWithId(1, parent
.get()));
237 Window
* w11
= CreateTestWindowWithId(11, parent
.get());
238 scoped_ptr
<Window
> w2(CreateTestWindowWithId(2, parent
.get()));
239 Window
* w21
= CreateTestWindowWithId(21, parent
.get());
240 Window
* w211
= CreateTestWindowWithId(211, parent
.get());
241 Window
* w212
= CreateTestWindowWithId(212, parent
.get());
242 Window
* w213
= CreateTestWindowWithId(213, parent
.get());
243 Window
* w22
= CreateTestWindowWithId(22, parent
.get());
244 ASSERT_EQ(8u, parent
->children().size());
246 // w11 is now owned by w1.
247 AddTransientChild(w1
.get(), w11
);
248 // w21 is now owned by w2.
249 AddTransientChild(w2
.get(), w21
);
250 // w22 is now owned by w2.
251 AddTransientChild(w2
.get(), w22
);
252 // w211 is now owned by w21.
253 AddTransientChild(w21
, w211
);
254 // w212 is now owned by w21.
255 AddTransientChild(w21
, w212
);
256 // w213 is now owned by w21.
257 AddTransientChild(w21
, w213
);
258 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent
.get()));
260 // Stack w2 at the bottom, this should force w11 to be last (on top of w1).
261 // This also tests that the order in children_ array rather than in
262 // transient_children_ array is used when reinserting transient children.
263 // If transient_children_ array was used '22' would be following '21'.
264 parent
->StackChildAtBottom(w2
.get());
265 EXPECT_EQ(w11
, parent
->children().back());
266 EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent
.get()));
268 parent
->StackChildAtBottom(w1
.get());
269 EXPECT_EQ(w22
, parent
->children().back());
270 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent
.get()));
272 parent
->StackChildBelow(w21
, w1
.get());
273 EXPECT_EQ(w11
, parent
->children().back());
274 EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent
.get()));
276 parent
->StackChildBelow(w11
, w2
.get());
277 EXPECT_EQ(w22
, parent
->children().back());
278 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent
.get()));
280 parent
->StackChildBelow(w22
, w21
);
281 EXPECT_EQ(w213
, parent
->children().back());
282 EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent
.get()));
284 parent
->StackChildBelow(w21
, w11
);
285 EXPECT_EQ(w11
, parent
->children().back());
286 EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent
.get()));
288 parent
->StackChildBelow(w213
, w211
);
289 EXPECT_EQ(w11
, parent
->children().back());
290 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent
.get()));
292 // No change when stacking a transient parent below its transient child.
293 parent
->StackChildBelow(w21
, w211
);
294 EXPECT_EQ(w11
, parent
->children().back());
295 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent
.get()));
297 parent
->StackChildBelow(w1
.get(), w2
.get());
298 EXPECT_EQ(w212
, parent
->children().back());
299 EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent
.get()));
301 parent
->StackChildBelow(w213
, w11
);
302 EXPECT_EQ(w11
, parent
->children().back());
303 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent
.get()));
306 // Tests that transient windows are stacked properly when created.
307 TEST_F(TransientWindowManagerTest
, StackUponCreation
) {
308 scoped_ptr
<Window
> window0(CreateTestWindowWithId(0, root_window()));
309 scoped_ptr
<Window
> window1(CreateTestWindowWithId(1, root_window()));
311 scoped_ptr
<Window
> window2(CreateTransientChild(2, window0
.get()));
312 EXPECT_EQ("0 2 1", ChildWindowIDsAsString(root_window()));
315 // Tests that windows are restacked properly after a call to AddTransientChild()
316 // or RemoveTransientChild().
317 TEST_F(TransientWindowManagerTest
, RestackUponAddOrRemoveTransientChild
) {
318 scoped_ptr
<Window
> windows
[4];
319 for (int i
= 0; i
< 4; i
++)
320 windows
[i
].reset(CreateTestWindowWithId(i
, root_window()));
321 EXPECT_EQ("0 1 2 3", ChildWindowIDsAsString(root_window()));
323 AddTransientChild(windows
[0].get(), windows
[2].get());
324 EXPECT_EQ("0 2 1 3", ChildWindowIDsAsString(root_window()));
326 AddTransientChild(windows
[0].get(), windows
[3].get());
327 EXPECT_EQ("0 2 3 1", ChildWindowIDsAsString(root_window()));
329 RemoveTransientChild(windows
[0].get(), windows
[2].get());
330 EXPECT_EQ("0 3 2 1", ChildWindowIDsAsString(root_window()));
332 RemoveTransientChild(windows
[0].get(), windows
[3].get());
333 EXPECT_EQ("0 3 2 1", ChildWindowIDsAsString(root_window()));
338 // Used by NotifyDelegateAfterDeletingTransients. Adds a string to a vector when
339 // OnWindowDestroyed() is invoked so that destruction order can be verified.
340 class DestroyedTrackingDelegate
: public aura::test::TestWindowDelegate
{
342 explicit DestroyedTrackingDelegate(const std::string
& name
,
343 std::vector
<std::string
>* results
)
347 void OnWindowDestroyed(aura::Window
* window
) override
{
348 results_
->push_back(name_
);
352 const std::string name_
;
353 std::vector
<std::string
>* results_
;
355 DISALLOW_COPY_AND_ASSIGN(DestroyedTrackingDelegate
);
360 // Verifies the delegate is notified of destruction after transients are
362 TEST_F(TransientWindowManagerTest
, NotifyDelegateAfterDeletingTransients
) {
363 std::vector
<std::string
> destruction_order
;
365 DestroyedTrackingDelegate
parent_delegate("parent", &destruction_order
);
366 scoped_ptr
<Window
> parent(new Window(&parent_delegate
));
367 parent
->Init(ui::LAYER_NOT_DRAWN
);
369 DestroyedTrackingDelegate
transient_delegate("transient", &destruction_order
);
370 Window
* transient
= new Window(&transient_delegate
); // Owned by |parent|.
371 transient
->Init(ui::LAYER_NOT_DRAWN
);
372 AddTransientChild(parent
.get(), transient
);
375 ASSERT_EQ(2u, destruction_order
.size());
376 EXPECT_EQ("transient", destruction_order
[0]);
377 EXPECT_EQ("parent", destruction_order
[1]);
380 TEST_F(TransientWindowManagerTest
,
381 StackTransientsLayersRelativeToOtherTransients
) {
382 // Create a window with several transients, then a couple windows on top.
383 scoped_ptr
<Window
> window1(CreateTestWindowWithId(1, root_window()));
384 scoped_ptr
<Window
> window11(CreateTransientChild(11, window1
.get()));
385 scoped_ptr
<Window
> window12(CreateTransientChild(12, window1
.get()));
386 scoped_ptr
<Window
> window13(CreateTransientChild(13, window1
.get()));
388 EXPECT_EQ("1 11 12 13", ChildWindowIDsAsString(root_window()));
390 // Stack 11 above 12.
391 root_window()->StackChildAbove(window11
.get(), window12
.get());
392 EXPECT_EQ("1 12 11 13", ChildWindowIDsAsString(root_window()));
394 // Stack 13 below 12.
395 root_window()->StackChildBelow(window13
.get(), window12
.get());
396 EXPECT_EQ("1 13 12 11", ChildWindowIDsAsString(root_window()));
399 root_window()->StackChildAbove(window11
.get(), window1
.get());
400 EXPECT_EQ("1 11 13 12", ChildWindowIDsAsString(root_window()));
402 // Stack 12 below 13.
403 root_window()->StackChildBelow(window12
.get(), window13
.get());
404 EXPECT_EQ("1 11 12 13", ChildWindowIDsAsString(root_window()));
407 // Verifies TransientWindowObserver is notified appropriately.
408 TEST_F(TransientWindowManagerTest
, TransientWindowObserverNotified
) {
409 scoped_ptr
<Window
> parent(CreateTestWindowWithId(0, root_window()));
410 scoped_ptr
<Window
> w1(CreateTestWindowWithId(1, parent
.get()));
412 TestTransientWindowObserver test_observer
;
413 TransientWindowManager::Get(parent
.get())->AddObserver(&test_observer
);
415 AddTransientChild(parent
.get(), w1
.get());
416 EXPECT_EQ(1, test_observer
.add_count());
417 EXPECT_EQ(0, test_observer
.remove_count());
419 RemoveTransientChild(parent
.get(), w1
.get());
420 EXPECT_EQ(1, test_observer
.add_count());
421 EXPECT_EQ(1, test_observer
.remove_count());
423 TransientWindowManager::Get(parent
.get())->RemoveObserver(&test_observer
);