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