OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "ui/views/corewm/transient_window_manager.h" | |
6 | |
7 #include "ui/aura/client/visibility_client.h" | |
8 #include "ui/aura/client/window_tree_client.h" | |
9 #include "ui/aura/layout_manager.h" | |
10 #include "ui/aura/test/aura_test_base.h" | |
11 #include "ui/aura/test/test_windows.h" | |
12 #include "ui/aura/window.h" | |
13 #include "ui/views/corewm/transient_window_observer.h" | |
14 #include "ui/views/corewm/window_util.h" | |
15 #include "ui/views/corewm/wm_state.h" | |
16 | |
17 using aura::Window; | |
18 | |
19 using aura::test::ChildWindowIDsAsString; | |
20 using aura::test::CreateTestWindowWithId; | |
21 | |
22 namespace views { | |
23 namespace corewm { | |
24 | |
25 class TestTransientWindowObserver : public TransientWindowObserver { | |
26 public: | |
27 TestTransientWindowObserver() : add_count_(0), remove_count_(0) { | |
28 } | |
29 | |
30 virtual ~TestTransientWindowObserver() { | |
31 } | |
32 | |
33 int add_count() const { return add_count_; } | |
34 int remove_count() const { return remove_count_; } | |
35 | |
36 // TransientWindowObserver overrides: | |
37 virtual void OnTransientChildAdded(Window* window, | |
38 Window* transient) OVERRIDE { | |
39 add_count_++; | |
40 } | |
41 virtual void OnTransientChildRemoved(Window* window, | |
42 Window* transient) OVERRIDE { | |
43 remove_count_++; | |
44 } | |
45 | |
46 private: | |
47 int add_count_; | |
48 int remove_count_; | |
49 | |
50 DISALLOW_COPY_AND_ASSIGN(TestTransientWindowObserver); | |
51 }; | |
52 | |
53 class TransientWindowManagerTest : public aura::test::AuraTestBase { | |
54 public: | |
55 TransientWindowManagerTest() {} | |
56 virtual ~TransientWindowManagerTest() {} | |
57 | |
58 virtual void SetUp() OVERRIDE { | |
59 AuraTestBase::SetUp(); | |
60 wm_state_.reset(new views::corewm::WMState); | |
61 } | |
62 | |
63 virtual void TearDown() OVERRIDE { | |
64 wm_state_.reset(); | |
65 AuraTestBase::TearDown(); | |
66 } | |
67 | |
68 protected: | |
69 // Creates a transient window that is transient to |parent|. | |
70 Window* CreateTransientChild(int id, Window* parent) { | |
71 Window* window = new Window(NULL); | |
72 window->set_id(id); | |
73 window->SetType(ui::wm::WINDOW_TYPE_NORMAL); | |
74 window->Init(aura::WINDOW_LAYER_TEXTURED); | |
75 aura::client::ParentWindowWithContext(window, root_window(), gfx::Rect()); | |
76 AddTransientChild(parent, window); | |
77 return window; | |
78 } | |
79 | |
80 private: | |
81 scoped_ptr<views::corewm::WMState> wm_state_; | |
82 | |
83 DISALLOW_COPY_AND_ASSIGN(TransientWindowManagerTest); | |
84 }; | |
85 | |
86 // Various assertions for transient children. | |
87 TEST_F(TransientWindowManagerTest, TransientChildren) { | |
88 scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window())); | |
89 scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get())); | |
90 scoped_ptr<Window> w3(CreateTestWindowWithId(3, parent.get())); | |
91 Window* w2 = CreateTestWindowWithId(2, parent.get()); | |
92 // w2 is now owned by w1. | |
93 AddTransientChild(w1.get(), w2); | |
94 // Stack w1 at the top (end), this should force w2 to be last (on top of w1). | |
95 parent->StackChildAtTop(w1.get()); | |
96 ASSERT_EQ(3u, parent->children().size()); | |
97 EXPECT_EQ(w2, parent->children().back()); | |
98 | |
99 // Destroy w1, which should also destroy w3 (since it's a transient child). | |
100 w1.reset(); | |
101 w2 = NULL; | |
102 ASSERT_EQ(1u, parent->children().size()); | |
103 EXPECT_EQ(w3.get(), parent->children()[0]); | |
104 | |
105 w1.reset(CreateTestWindowWithId(4, parent.get())); | |
106 w2 = CreateTestWindowWithId(5, w3.get()); | |
107 AddTransientChild(w1.get(), w2); | |
108 parent->StackChildAtTop(w3.get()); | |
109 // Stack w1 at the top (end), this shouldn't affect w2 since it has a | |
110 // different parent. | |
111 parent->StackChildAtTop(w1.get()); | |
112 ASSERT_EQ(2u, parent->children().size()); | |
113 EXPECT_EQ(w3.get(), parent->children()[0]); | |
114 EXPECT_EQ(w1.get(), parent->children()[1]); | |
115 | |
116 // Hiding parent should hide transient children. | |
117 EXPECT_TRUE(w2->IsVisible()); | |
118 w1->Hide(); | |
119 EXPECT_FALSE(w2->IsVisible()); | |
120 } | |
121 | |
122 // Tests that transient children are stacked as a unit when using stack above. | |
123 TEST_F(TransientWindowManagerTest, TransientChildrenGroupAbove) { | |
124 scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window())); | |
125 scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get())); | |
126 Window* w11 = CreateTestWindowWithId(11, parent.get()); | |
127 scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get())); | |
128 Window* w21 = CreateTestWindowWithId(21, parent.get()); | |
129 Window* w211 = CreateTestWindowWithId(211, parent.get()); | |
130 Window* w212 = CreateTestWindowWithId(212, parent.get()); | |
131 Window* w213 = CreateTestWindowWithId(213, parent.get()); | |
132 Window* w22 = CreateTestWindowWithId(22, parent.get()); | |
133 ASSERT_EQ(8u, parent->children().size()); | |
134 | |
135 // w11 is now owned by w1. | |
136 AddTransientChild(w1.get(), w11); | |
137 // w21 is now owned by w2. | |
138 AddTransientChild(w2.get(), w21); | |
139 // w22 is now owned by w2. | |
140 AddTransientChild(w2.get(), w22); | |
141 // w211 is now owned by w21. | |
142 AddTransientChild(w21, w211); | |
143 // w212 is now owned by w21. | |
144 AddTransientChild(w21, w212); | |
145 // w213 is now owned by w21. | |
146 AddTransientChild(w21, w213); | |
147 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); | |
148 | |
149 // Stack w1 at the top (end), this should force w11 to be last (on top of w1). | |
150 parent->StackChildAtTop(w1.get()); | |
151 EXPECT_EQ(w11, parent->children().back()); | |
152 EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); | |
153 | |
154 // This tests that the order in children_ array rather than in | |
155 // transient_children_ array is used when reinserting transient children. | |
156 // If transient_children_ array was used '22' would be following '21'. | |
157 parent->StackChildAtTop(w2.get()); | |
158 EXPECT_EQ(w22, parent->children().back()); | |
159 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); | |
160 | |
161 parent->StackChildAbove(w11, w2.get()); | |
162 EXPECT_EQ(w11, parent->children().back()); | |
163 EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); | |
164 | |
165 parent->StackChildAbove(w21, w1.get()); | |
166 EXPECT_EQ(w22, parent->children().back()); | |
167 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); | |
168 | |
169 parent->StackChildAbove(w21, w22); | |
170 EXPECT_EQ(w213, parent->children().back()); | |
171 EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get())); | |
172 | |
173 parent->StackChildAbove(w11, w21); | |
174 EXPECT_EQ(w11, parent->children().back()); | |
175 EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get())); | |
176 | |
177 parent->StackChildAbove(w213, w21); | |
178 EXPECT_EQ(w11, parent->children().back()); | |
179 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); | |
180 | |
181 // No change when stacking a transient parent above its transient child. | |
182 parent->StackChildAbove(w21, w211); | |
183 EXPECT_EQ(w11, parent->children().back()); | |
184 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); | |
185 | |
186 // This tests that the order in children_ array rather than in | |
187 // transient_children_ array is used when reinserting transient children. | |
188 // If transient_children_ array was used '22' would be following '21'. | |
189 parent->StackChildAbove(w2.get(), w1.get()); | |
190 EXPECT_EQ(w212, parent->children().back()); | |
191 EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get())); | |
192 | |
193 parent->StackChildAbove(w11, w213); | |
194 EXPECT_EQ(w11, parent->children().back()); | |
195 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); | |
196 } | |
197 | |
198 // Tests that transient children are stacked as a unit when using stack below. | |
199 TEST_F(TransientWindowManagerTest, TransientChildrenGroupBelow) { | |
200 scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window())); | |
201 scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get())); | |
202 Window* w11 = CreateTestWindowWithId(11, parent.get()); | |
203 scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get())); | |
204 Window* w21 = CreateTestWindowWithId(21, parent.get()); | |
205 Window* w211 = CreateTestWindowWithId(211, parent.get()); | |
206 Window* w212 = CreateTestWindowWithId(212, parent.get()); | |
207 Window* w213 = CreateTestWindowWithId(213, parent.get()); | |
208 Window* w22 = CreateTestWindowWithId(22, parent.get()); | |
209 ASSERT_EQ(8u, parent->children().size()); | |
210 | |
211 // w11 is now owned by w1. | |
212 AddTransientChild(w1.get(), w11); | |
213 // w21 is now owned by w2. | |
214 AddTransientChild(w2.get(), w21); | |
215 // w22 is now owned by w2. | |
216 AddTransientChild(w2.get(), w22); | |
217 // w211 is now owned by w21. | |
218 AddTransientChild(w21, w211); | |
219 // w212 is now owned by w21. | |
220 AddTransientChild(w21, w212); | |
221 // w213 is now owned by w21. | |
222 AddTransientChild(w21, w213); | |
223 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); | |
224 | |
225 // Stack w2 at the bottom, this should force w11 to be last (on top of w1). | |
226 // This also tests that the order in children_ array rather than in | |
227 // transient_children_ array is used when reinserting transient children. | |
228 // If transient_children_ array was used '22' would be following '21'. | |
229 parent->StackChildAtBottom(w2.get()); | |
230 EXPECT_EQ(w11, parent->children().back()); | |
231 EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); | |
232 | |
233 parent->StackChildAtBottom(w1.get()); | |
234 EXPECT_EQ(w22, parent->children().back()); | |
235 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); | |
236 | |
237 parent->StackChildBelow(w21, w1.get()); | |
238 EXPECT_EQ(w11, parent->children().back()); | |
239 EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); | |
240 | |
241 parent->StackChildBelow(w11, w2.get()); | |
242 EXPECT_EQ(w22, parent->children().back()); | |
243 EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); | |
244 | |
245 parent->StackChildBelow(w22, w21); | |
246 EXPECT_EQ(w213, parent->children().back()); | |
247 EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get())); | |
248 | |
249 parent->StackChildBelow(w21, w11); | |
250 EXPECT_EQ(w11, parent->children().back()); | |
251 EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get())); | |
252 | |
253 parent->StackChildBelow(w213, w211); | |
254 EXPECT_EQ(w11, parent->children().back()); | |
255 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); | |
256 | |
257 // No change when stacking a transient parent below its transient child. | |
258 parent->StackChildBelow(w21, w211); | |
259 EXPECT_EQ(w11, parent->children().back()); | |
260 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); | |
261 | |
262 parent->StackChildBelow(w1.get(), w2.get()); | |
263 EXPECT_EQ(w212, parent->children().back()); | |
264 EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get())); | |
265 | |
266 parent->StackChildBelow(w213, w11); | |
267 EXPECT_EQ(w11, parent->children().back()); | |
268 EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); | |
269 } | |
270 | |
271 namespace { | |
272 | |
273 // Used by NotifyDelegateAfterDeletingTransients. Adds a string to a vector when | |
274 // OnWindowDestroyed() is invoked so that destruction order can be verified. | |
275 class DestroyedTrackingDelegate : public aura::test::TestWindowDelegate { | |
276 public: | |
277 explicit DestroyedTrackingDelegate(const std::string& name, | |
278 std::vector<std::string>* results) | |
279 : name_(name), | |
280 results_(results) {} | |
281 | |
282 virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE { | |
283 results_->push_back(name_); | |
284 } | |
285 | |
286 private: | |
287 const std::string name_; | |
288 std::vector<std::string>* results_; | |
289 | |
290 DISALLOW_COPY_AND_ASSIGN(DestroyedTrackingDelegate); | |
291 }; | |
292 | |
293 } // namespace | |
294 | |
295 // Verifies the delegate is notified of destruction after transients are | |
296 // destroyed. | |
297 TEST_F(TransientWindowManagerTest, NotifyDelegateAfterDeletingTransients) { | |
298 std::vector<std::string> destruction_order; | |
299 | |
300 DestroyedTrackingDelegate parent_delegate("parent", &destruction_order); | |
301 scoped_ptr<Window> parent(new Window(&parent_delegate)); | |
302 parent->Init(aura::WINDOW_LAYER_NOT_DRAWN); | |
303 | |
304 DestroyedTrackingDelegate transient_delegate("transient", &destruction_order); | |
305 Window* transient = new Window(&transient_delegate); // Owned by |parent|. | |
306 transient->Init(aura::WINDOW_LAYER_NOT_DRAWN); | |
307 AddTransientChild(parent.get(), transient); | |
308 parent.reset(); | |
309 | |
310 ASSERT_EQ(2u, destruction_order.size()); | |
311 EXPECT_EQ("transient", destruction_order[0]); | |
312 EXPECT_EQ("parent", destruction_order[1]); | |
313 } | |
314 | |
315 TEST_F(TransientWindowManagerTest, StackTransientsWhoseLayersHaveNoDelegate) { | |
316 // Create a window with several transients, then a couple windows on top. | |
317 scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window())); | |
318 scoped_ptr<Window> window11(CreateTransientChild(11, window1.get())); | |
319 scoped_ptr<Window> window12(CreateTransientChild(12, window1.get())); | |
320 scoped_ptr<Window> window13(CreateTransientChild(13, window1.get())); | |
321 scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window())); | |
322 scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window())); | |
323 | |
324 EXPECT_EQ("1 11 12 13 2 3", ChildWindowIDsAsString(root_window())); | |
325 | |
326 // Remove the delegates of a couple of transients, as if they are closing | |
327 // and animating out. | |
328 window11->layer()->set_delegate(NULL); | |
329 window13->layer()->set_delegate(NULL); | |
330 | |
331 // Move window1 to the front. All transients should move with it, and their | |
332 // order should be preserved. | |
333 root_window()->StackChildAtTop(window1.get()); | |
334 | |
335 EXPECT_EQ("2 3 1 11 12 13", ChildWindowIDsAsString(root_window())); | |
336 } | |
337 | |
338 TEST_F(TransientWindowManagerTest, | |
339 StackTransientsLayersRelativeToOtherTransients) { | |
340 // Create a window with several transients, then a couple windows on top. | |
341 scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window())); | |
342 scoped_ptr<Window> window11(CreateTransientChild(11, window1.get())); | |
343 scoped_ptr<Window> window12(CreateTransientChild(12, window1.get())); | |
344 scoped_ptr<Window> window13(CreateTransientChild(13, window1.get())); | |
345 | |
346 EXPECT_EQ("1 11 12 13", ChildWindowIDsAsString(root_window())); | |
347 | |
348 // Stack 11 above 12. | |
349 root_window()->StackChildAbove(window11.get(), window12.get()); | |
350 EXPECT_EQ("1 12 11 13", ChildWindowIDsAsString(root_window())); | |
351 | |
352 // Stack 13 below 12. | |
353 root_window()->StackChildBelow(window13.get(), window12.get()); | |
354 EXPECT_EQ("1 13 12 11", ChildWindowIDsAsString(root_window())); | |
355 | |
356 // Stack 11 above 1. | |
357 root_window()->StackChildAbove(window11.get(), window1.get()); | |
358 EXPECT_EQ("1 11 13 12", ChildWindowIDsAsString(root_window())); | |
359 | |
360 // Stack 12 below 13. | |
361 root_window()->StackChildBelow(window12.get(), window13.get()); | |
362 EXPECT_EQ("1 11 12 13", ChildWindowIDsAsString(root_window())); | |
363 } | |
364 | |
365 TEST_F(TransientWindowManagerTest, | |
366 StackTransientsLayersRelativeToOtherTransientsNoLayerDelegate) { | |
367 // Create a window with several transients, then a couple windows on top. | |
368 scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window())); | |
369 scoped_ptr<Window> window11(CreateTransientChild(11, window1.get())); | |
370 scoped_ptr<Window> window12(CreateTransientChild(12, window1.get())); | |
371 scoped_ptr<Window> window13(CreateTransientChild(13, window1.get())); | |
372 scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window())); | |
373 scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window())); | |
374 | |
375 EXPECT_EQ("1 11 12 13 2 3", ChildWindowIDsAsString(root_window())); | |
376 | |
377 window1->layer()->set_delegate(NULL); | |
378 | |
379 // Stack 1 at top. | |
380 root_window()->StackChildAtTop(window1.get()); | |
381 EXPECT_EQ("2 3 1 11 12 13", ChildWindowIDsAsString(root_window())); | |
382 } | |
383 | |
384 class StackingMadrigalLayoutManager : public aura::LayoutManager { | |
385 public: | |
386 explicit StackingMadrigalLayoutManager(Window* root_window) | |
387 : root_window_(root_window) { | |
388 root_window_->SetLayoutManager(this); | |
389 } | |
390 virtual ~StackingMadrigalLayoutManager() { | |
391 } | |
392 | |
393 private: | |
394 // Overridden from LayoutManager: | |
395 virtual void OnWindowResized() OVERRIDE {} | |
396 virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {} | |
397 virtual void OnWillRemoveWindowFromLayout(Window* child) OVERRIDE {} | |
398 virtual void OnWindowRemovedFromLayout(Window* child) OVERRIDE {} | |
399 virtual void OnChildWindowVisibilityChanged(Window* child, | |
400 bool visible) OVERRIDE { | |
401 Window::Windows::const_iterator it = root_window_->children().begin(); | |
402 Window* last_window = NULL; | |
403 for (; it != root_window_->children().end(); ++it) { | |
404 if (*it == child && last_window) { | |
405 if (!visible) | |
406 root_window_->StackChildAbove(last_window, *it); | |
407 else | |
408 root_window_->StackChildAbove(*it, last_window); | |
409 break; | |
410 } | |
411 last_window = *it; | |
412 } | |
413 } | |
414 virtual void SetChildBounds(Window* child, | |
415 const gfx::Rect& requested_bounds) OVERRIDE { | |
416 SetChildBoundsDirect(child, requested_bounds); | |
417 } | |
418 | |
419 Window* root_window_; | |
420 | |
421 DISALLOW_COPY_AND_ASSIGN(StackingMadrigalLayoutManager); | |
422 }; | |
423 | |
424 class StackingMadrigalVisibilityClient : public aura::client::VisibilityClient { | |
425 public: | |
426 explicit StackingMadrigalVisibilityClient(Window* root_window) | |
427 : ignored_window_(NULL) { | |
428 aura::client::SetVisibilityClient(root_window, this); | |
429 } | |
430 virtual ~StackingMadrigalVisibilityClient() { | |
431 } | |
432 | |
433 void set_ignored_window(Window* ignored_window) { | |
434 ignored_window_ = ignored_window; | |
435 } | |
436 | |
437 private: | |
438 // Overridden from client::VisibilityClient: | |
439 virtual void UpdateLayerVisibility(Window* window, bool visible) OVERRIDE { | |
440 if (!visible) { | |
441 if (window == ignored_window_) | |
442 window->layer()->set_delegate(NULL); | |
443 else | |
444 window->layer()->SetVisible(visible); | |
445 } else { | |
446 window->layer()->SetVisible(visible); | |
447 } | |
448 } | |
449 | |
450 Window* ignored_window_; | |
451 | |
452 DISALLOW_COPY_AND_ASSIGN(StackingMadrigalVisibilityClient); | |
453 }; | |
454 | |
455 // This test attempts to reconstruct a circumstance that can happen when the | |
456 // aura client attempts to manipulate the visibility and delegate of a layer | |
457 // independent of window visibility. | |
458 // A use case is where the client attempts to keep a window visible onscreen | |
459 // even after code has called Hide() on the window. The use case for this would | |
460 // be that window hides are animated (e.g. the window fades out). To prevent | |
461 // spurious updating the client code may also clear window's layer's delegate, | |
462 // so that the window cannot attempt to paint or update it further. The window | |
463 // uses the presence of a NULL layer delegate as a signal in stacking to note | |
464 // that the window is being manipulated by such a use case and its stacking | |
465 // should not be adjusted. | |
466 // One issue that can arise when a window opens two transient children, and the | |
467 // first is hidden. Subsequent attempts to activate the transient parent can | |
468 // result in the transient parent being stacked above the second transient | |
469 // child. A fix is made to Window::StackAbove to prevent this, and this test | |
470 // verifies this fix. | |
471 TEST_F(TransientWindowManagerTest, StackingMadrigal) { | |
472 new StackingMadrigalLayoutManager(root_window()); | |
473 StackingMadrigalVisibilityClient visibility_client(root_window()); | |
474 | |
475 scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window())); | |
476 scoped_ptr<Window> window11(CreateTransientChild(11, window1.get())); | |
477 | |
478 visibility_client.set_ignored_window(window11.get()); | |
479 | |
480 window11->Show(); | |
481 window11->Hide(); | |
482 | |
483 // As a transient, window11 should still be stacked above window1, even when | |
484 // hidden. | |
485 EXPECT_TRUE(aura::test::WindowIsAbove(window11.get(), window1.get())); | |
486 EXPECT_TRUE(aura::test::LayerIsAbove(window11.get(), window1.get())); | |
487 | |
488 // A new transient should still be above window1. It will appear behind | |
489 // window11 because we don't stack windows on top of targets with NULL | |
490 // delegates. | |
491 scoped_ptr<Window> window12(CreateTransientChild(12, window1.get())); | |
492 window12->Show(); | |
493 | |
494 EXPECT_TRUE(aura::test::WindowIsAbove(window12.get(), window1.get())); | |
495 EXPECT_TRUE(aura::test::LayerIsAbove(window12.get(), window1.get())); | |
496 | |
497 // In earlier versions of the StackChildAbove() method, attempting to stack | |
498 // window1 above window12 at this point would actually restack the layers | |
499 // resulting in window12's layer being below window1's layer (though the | |
500 // windows themselves would still be correctly stacked, so events would pass | |
501 // through.) | |
502 root_window()->StackChildAbove(window1.get(), window12.get()); | |
503 | |
504 // Both window12 and its layer should be stacked above window1. | |
505 EXPECT_TRUE(aura::test::WindowIsAbove(window12.get(), window1.get())); | |
506 EXPECT_TRUE(aura::test::LayerIsAbove(window12.get(), window1.get())); | |
507 } | |
508 | |
509 // Test for an issue where attempting to stack a primary window on top of a | |
510 // transient with a NULL layer delegate causes that primary window to be moved, | |
511 // but the layer order not changed to match. http://crbug.com/112562 | |
512 TEST_F(TransientWindowManagerTest, StackOverClosingTransient) { | |
513 scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window())); | |
514 scoped_ptr<Window> transient1(CreateTransientChild(11, window1.get())); | |
515 scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window())); | |
516 scoped_ptr<Window> transient2(CreateTransientChild(21, window2.get())); | |
517 | |
518 // Both windows and layers are stacked in creation order. | |
519 Window* root = root_window(); | |
520 ASSERT_EQ(4u, root->children().size()); | |
521 EXPECT_EQ(root->children()[0], window1.get()); | |
522 EXPECT_EQ(root->children()[1], transient1.get()); | |
523 EXPECT_EQ(root->children()[2], window2.get()); | |
524 EXPECT_EQ(root->children()[3], transient2.get()); | |
525 ASSERT_EQ(4u, root->layer()->children().size()); | |
526 EXPECT_EQ(root->layer()->children()[0], window1->layer()); | |
527 EXPECT_EQ(root->layer()->children()[1], transient1->layer()); | |
528 EXPECT_EQ(root->layer()->children()[2], window2->layer()); | |
529 EXPECT_EQ(root->layer()->children()[3], transient2->layer()); | |
530 EXPECT_EQ("1 11 2 21", ChildWindowIDsAsString(root_window())); | |
531 | |
532 // This brings window1 and its transient to the front. | |
533 root->StackChildAtTop(window1.get()); | |
534 EXPECT_EQ("2 21 1 11", ChildWindowIDsAsString(root_window())); | |
535 | |
536 EXPECT_EQ(root->children()[0], window2.get()); | |
537 EXPECT_EQ(root->children()[1], transient2.get()); | |
538 EXPECT_EQ(root->children()[2], window1.get()); | |
539 EXPECT_EQ(root->children()[3], transient1.get()); | |
540 EXPECT_EQ(root->layer()->children()[0], window2->layer()); | |
541 EXPECT_EQ(root->layer()->children()[1], transient2->layer()); | |
542 EXPECT_EQ(root->layer()->children()[2], window1->layer()); | |
543 EXPECT_EQ(root->layer()->children()[3], transient1->layer()); | |
544 | |
545 // Pretend we're closing the top-most transient, then bring window2 to the | |
546 // front. This mimics activating a browser window while the status bubble | |
547 // is fading out. The transient should stay topmost. | |
548 transient1->layer()->set_delegate(NULL); | |
549 root->StackChildAtTop(window2.get()); | |
550 | |
551 EXPECT_EQ(root->children()[0], window1.get()); | |
552 EXPECT_EQ(root->children()[1], window2.get()); | |
553 EXPECT_EQ(root->children()[2], transient2.get()); | |
554 EXPECT_EQ(root->children()[3], transient1.get()); | |
555 EXPECT_EQ(root->layer()->children()[0], window1->layer()); | |
556 EXPECT_EQ(root->layer()->children()[1], window2->layer()); | |
557 EXPECT_EQ(root->layer()->children()[2], transient2->layer()); | |
558 EXPECT_EQ(root->layer()->children()[3], transient1->layer()); | |
559 | |
560 // Close the transient. Remaining windows are stable. | |
561 transient1.reset(); | |
562 | |
563 ASSERT_EQ(3u, root->children().size()); | |
564 EXPECT_EQ(root->children()[0], window1.get()); | |
565 EXPECT_EQ(root->children()[1], window2.get()); | |
566 EXPECT_EQ(root->children()[2], transient2.get()); | |
567 ASSERT_EQ(3u, root->layer()->children().size()); | |
568 EXPECT_EQ(root->layer()->children()[0], window1->layer()); | |
569 EXPECT_EQ(root->layer()->children()[1], window2->layer()); | |
570 EXPECT_EQ(root->layer()->children()[2], transient2->layer()); | |
571 | |
572 // Open another window on top. | |
573 scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window())); | |
574 | |
575 ASSERT_EQ(4u, root->children().size()); | |
576 EXPECT_EQ(root->children()[0], window1.get()); | |
577 EXPECT_EQ(root->children()[1], window2.get()); | |
578 EXPECT_EQ(root->children()[2], transient2.get()); | |
579 EXPECT_EQ(root->children()[3], window3.get()); | |
580 ASSERT_EQ(4u, root->layer()->children().size()); | |
581 EXPECT_EQ(root->layer()->children()[0], window1->layer()); | |
582 EXPECT_EQ(root->layer()->children()[1], window2->layer()); | |
583 EXPECT_EQ(root->layer()->children()[2], transient2->layer()); | |
584 EXPECT_EQ(root->layer()->children()[3], window3->layer()); | |
585 | |
586 // Pretend we're closing the topmost non-transient window, then bring | |
587 // window2 to the top. It should not move. | |
588 window3->layer()->set_delegate(NULL); | |
589 root->StackChildAtTop(window2.get()); | |
590 | |
591 ASSERT_EQ(4u, root->children().size()); | |
592 EXPECT_EQ(root->children()[0], window1.get()); | |
593 EXPECT_EQ(root->children()[1], window2.get()); | |
594 EXPECT_EQ(root->children()[2], transient2.get()); | |
595 EXPECT_EQ(root->children()[3], window3.get()); | |
596 ASSERT_EQ(4u, root->layer()->children().size()); | |
597 EXPECT_EQ(root->layer()->children()[0], window1->layer()); | |
598 EXPECT_EQ(root->layer()->children()[1], window2->layer()); | |
599 EXPECT_EQ(root->layer()->children()[2], transient2->layer()); | |
600 EXPECT_EQ(root->layer()->children()[3], window3->layer()); | |
601 | |
602 // Bring window1 to the top. It should move ahead of window2, but not | |
603 // ahead of window3 (with NULL delegate). | |
604 root->StackChildAtTop(window1.get()); | |
605 | |
606 ASSERT_EQ(4u, root->children().size()); | |
607 EXPECT_EQ(root->children()[0], window2.get()); | |
608 EXPECT_EQ(root->children()[1], transient2.get()); | |
609 EXPECT_EQ(root->children()[2], window1.get()); | |
610 EXPECT_EQ(root->children()[3], window3.get()); | |
611 ASSERT_EQ(4u, root->layer()->children().size()); | |
612 EXPECT_EQ(root->layer()->children()[0], window2->layer()); | |
613 EXPECT_EQ(root->layer()->children()[1], transient2->layer()); | |
614 EXPECT_EQ(root->layer()->children()[2], window1->layer()); | |
615 EXPECT_EQ(root->layer()->children()[3], window3->layer()); | |
616 } | |
617 | |
618 // Verifies TransientWindowObserver is notified appropriately. | |
619 TEST_F(TransientWindowManagerTest, TransientWindowObserverNotified) { | |
620 scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window())); | |
621 scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get())); | |
622 | |
623 TestTransientWindowObserver test_observer; | |
624 TransientWindowManager::Get(parent.get())->AddObserver(&test_observer); | |
625 | |
626 AddTransientChild(parent.get(), w1.get()); | |
627 EXPECT_EQ(1, test_observer.add_count()); | |
628 EXPECT_EQ(0, test_observer.remove_count()); | |
629 | |
630 RemoveTransientChild(parent.get(), w1.get()); | |
631 EXPECT_EQ(1, test_observer.add_count()); | |
632 EXPECT_EQ(1, test_observer.remove_count()); | |
633 | |
634 TransientWindowManager::Get(parent.get())->RemoveObserver(&test_observer); | |
635 } | |
636 | |
637 } // namespace corewm | |
638 } // namespace views | |
OLD | NEW |