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