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