OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 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 "ash/wm/solo_window_tracker.h" |
| 6 |
| 7 #include "ash/ash_constants.h" |
| 8 #include "ash/ash_switches.h" |
| 9 #include "ash/root_window_controller.h" |
| 10 #include "ash/screen_ash.h" |
| 11 #include "ash/shell.h" |
| 12 #include "ash/shell_window_ids.h" |
| 13 #include "ash/test/ash_test_base.h" |
| 14 #include "ash/wm/window_resizer.h" |
| 15 #include "ash/wm/window_state.h" |
| 16 #include "base/memory/scoped_ptr.h" |
| 17 #include "ui/aura/client/aura_constants.h" |
| 18 #include "ui/aura/root_window.h" |
| 19 #include "ui/aura/test/event_generator.h" |
| 20 #include "ui/aura/window.h" |
| 21 #include "ui/aura/window_observer.h" |
| 22 #include "ui/base/hit_test.h" |
| 23 #include "ui/gfx/screen.h" |
| 24 |
| 25 namespace ash { |
| 26 |
| 27 namespace { |
| 28 |
| 29 class WindowRepaintChecker : public aura::WindowObserver { |
| 30 public: |
| 31 explicit WindowRepaintChecker(aura::Window* window) |
| 32 : window_(window), |
| 33 is_paint_scheduled_(false) { |
| 34 window_->AddObserver(this); |
| 35 } |
| 36 |
| 37 virtual ~WindowRepaintChecker() { |
| 38 if (window_) |
| 39 window_->RemoveObserver(this); |
| 40 } |
| 41 |
| 42 bool IsPaintScheduledAndReset() { |
| 43 bool result = is_paint_scheduled_; |
| 44 is_paint_scheduled_ = false; |
| 45 return result; |
| 46 } |
| 47 |
| 48 private: |
| 49 // aura::WindowObserver overrides: |
| 50 virtual void OnWindowPaintScheduled(aura::Window* window, |
| 51 const gfx::Rect& region) OVERRIDE { |
| 52 is_paint_scheduled_ = true; |
| 53 } |
| 54 virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE { |
| 55 DCHECK_EQ(window_, window); |
| 56 window_ = NULL; |
| 57 } |
| 58 |
| 59 aura::Window* window_; |
| 60 bool is_paint_scheduled_; |
| 61 |
| 62 DISALLOW_COPY_AND_ASSIGN(WindowRepaintChecker); |
| 63 }; |
| 64 |
| 65 } // namespace |
| 66 |
| 67 class SoloWindowTrackerTest : public test::AshTestBase { |
| 68 public: |
| 69 SoloWindowTrackerTest() { |
| 70 } |
| 71 virtual ~SoloWindowTrackerTest() { |
| 72 } |
| 73 |
| 74 // Helpers methods to create test windows in the primary root window. |
| 75 aura::Window* CreateWindowInPrimary() { |
| 76 aura::Window* window = new aura::Window(NULL); |
| 77 window->SetType(ui::wm::WINDOW_TYPE_NORMAL); |
| 78 window->Init(aura::WINDOW_LAYER_TEXTURED); |
| 79 window->SetBounds(gfx::Rect(100, 100)); |
| 80 ParentWindowInPrimaryRootWindow(window); |
| 81 return window; |
| 82 } |
| 83 aura::Window* CreateAlwaysOnTopWindowInPrimary() { |
| 84 aura::Window* window = new aura::Window(NULL); |
| 85 window->SetType(ui::wm::WINDOW_TYPE_NORMAL); |
| 86 window->Init(aura::WINDOW_LAYER_TEXTURED); |
| 87 window->SetBounds(gfx::Rect(100, 100)); |
| 88 window->SetProperty(aura::client::kAlwaysOnTopKey, true); |
| 89 ParentWindowInPrimaryRootWindow(window); |
| 90 return window; |
| 91 } |
| 92 aura::Window* CreatePanelWindowInPrimary() { |
| 93 aura::Window* window = new aura::Window(NULL); |
| 94 window->SetType(ui::wm::WINDOW_TYPE_PANEL); |
| 95 window->Init(aura::WINDOW_LAYER_TEXTURED); |
| 96 window->SetBounds(gfx::Rect(100, 100)); |
| 97 ParentWindowInPrimaryRootWindow(window); |
| 98 return window; |
| 99 } |
| 100 |
| 101 // Drag |window| to the dock. |
| 102 void DockWindow(aura::Window* window) { |
| 103 // Because the tests use windows without delegates, |
| 104 // aura::test::EventGenerator cannot be used. |
| 105 gfx::Point drag_to = |
| 106 ash::ScreenAsh::GetDisplayBoundsInParent(window).top_right(); |
| 107 scoped_ptr<WindowResizer> resizer(CreateWindowResizer( |
| 108 window, |
| 109 window->bounds().origin(), |
| 110 HTCAPTION, |
| 111 aura::client::WINDOW_MOVE_SOURCE_MOUSE)); |
| 112 resizer->Drag(drag_to, 0); |
| 113 resizer->CompleteDrag(); |
| 114 EXPECT_EQ(internal::kShellWindowId_DockedContainer, |
| 115 window->parent()->id()); |
| 116 } |
| 117 |
| 118 // Drag |window| out of the dock. |
| 119 void UndockWindow(aura::Window* window) { |
| 120 gfx::Point drag_to = |
| 121 ash::ScreenAsh::GetDisplayWorkAreaBoundsInParent(window).top_right() - |
| 122 gfx::Vector2d(10, 0); |
| 123 scoped_ptr<WindowResizer> resizer(CreateWindowResizer( |
| 124 window, |
| 125 window->bounds().origin(), |
| 126 HTCAPTION, |
| 127 aura::client::WINDOW_MOVE_SOURCE_MOUSE)); |
| 128 resizer->Drag(drag_to, 0); |
| 129 resizer->CompleteDrag(); |
| 130 EXPECT_NE(internal::kShellWindowId_DockedContainer, |
| 131 window->parent()->id()); |
| 132 } |
| 133 |
| 134 // Returns the primary display. |
| 135 gfx::Display GetPrimaryDisplay() { |
| 136 return ash::Shell::GetInstance()->GetScreen()->GetPrimaryDisplay(); |
| 137 } |
| 138 |
| 139 // Returns the secondary display. |
| 140 gfx::Display GetSecondaryDisplay() { |
| 141 return ScreenAsh::GetSecondaryDisplay(); |
| 142 } |
| 143 |
| 144 // Returns the window which uses the solo header, if any, on the primary |
| 145 // display. |
| 146 aura::Window* GetWindowWithSoloHeaderInPrimary() { |
| 147 return GetWindowWithSoloHeader(Shell::GetPrimaryRootWindow()); |
| 148 } |
| 149 |
| 150 // Returns the window which uses the solo header, if any, in |root|. |
| 151 aura::Window* GetWindowWithSoloHeader(aura::Window* root) { |
| 152 SoloWindowTracker* solo_window_tracker = |
| 153 internal::GetRootWindowController(root)->solo_window_tracker(); |
| 154 return solo_window_tracker ? |
| 155 solo_window_tracker->GetWindowWithSoloHeader() : NULL; |
| 156 } |
| 157 |
| 158 private: |
| 159 DISALLOW_COPY_AND_ASSIGN(SoloWindowTrackerTest); |
| 160 }; |
| 161 |
| 162 TEST_F(SoloWindowTrackerTest, Basic) { |
| 163 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
| 164 w1->Show(); |
| 165 |
| 166 // We only have one window, so it should use a solo header. |
| 167 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 168 |
| 169 // Create a second window. |
| 170 scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); |
| 171 w2->Show(); |
| 172 |
| 173 // Now there are two windows, so we should not use solo headers. |
| 174 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 175 |
| 176 // Hide one window. Solo should be enabled. |
| 177 w2->Hide(); |
| 178 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 179 |
| 180 // Show that window. Solo should be disabled. |
| 181 w2->Show(); |
| 182 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 183 |
| 184 // Minimize the first window. Solo should be enabled. |
| 185 wm::GetWindowState(w1.get())->Minimize(); |
| 186 EXPECT_EQ(w2.get(), GetWindowWithSoloHeaderInPrimary()); |
| 187 |
| 188 // Close the minimized window. |
| 189 w1.reset(); |
| 190 EXPECT_EQ(w2.get(), GetWindowWithSoloHeaderInPrimary()); |
| 191 |
| 192 // Open an always-on-top window (which lives in a different container). |
| 193 scoped_ptr<aura::Window> w3(CreateAlwaysOnTopWindowInPrimary()); |
| 194 w3->Show(); |
| 195 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 196 |
| 197 // Close the always-on-top window. |
| 198 w3.reset(); |
| 199 EXPECT_EQ(w2.get(), GetWindowWithSoloHeaderInPrimary()); |
| 200 } |
| 201 |
| 202 // Test that docked windows never use the solo header and that the presence of a |
| 203 // docked window prevents all other windows from the using the solo window |
| 204 // header. |
| 205 TEST_F(SoloWindowTrackerTest, DockedWindow) { |
| 206 if (!switches::UseDockedWindows() || !SupportsHostWindowResize()) |
| 207 return; |
| 208 |
| 209 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
| 210 w1->Show(); |
| 211 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 212 |
| 213 DockWindow(w1.get()); |
| 214 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 215 |
| 216 UndockWindow(w1.get()); |
| 217 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 218 |
| 219 scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); |
| 220 w2->Show(); |
| 221 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 222 |
| 223 DockWindow(w2.get()); |
| 224 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 225 |
| 226 wm::GetWindowState(w2.get())->Minimize(); |
| 227 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 228 } |
| 229 |
| 230 // Panels should not "count" for computing solo window headers, and the panel |
| 231 // itself should never use the solo header. |
| 232 TEST_F(SoloWindowTrackerTest, Panel) { |
| 233 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
| 234 w1->Show(); |
| 235 |
| 236 // We only have one window, so it should use a solo header. |
| 237 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 238 |
| 239 // Create a panel window. |
| 240 scoped_ptr<aura::Window> w2(CreatePanelWindowInPrimary()); |
| 241 w2->Show(); |
| 242 |
| 243 // Despite two windows, the first window should still be considered "solo" |
| 244 // because panels aren't included in the computation. |
| 245 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 246 |
| 247 // Even after closing the first window, the panel is still not considered |
| 248 // solo. |
| 249 w1.reset(); |
| 250 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 251 } |
| 252 |
| 253 // Modal dialogs should not use solo headers. |
| 254 TEST_F(SoloWindowTrackerTest, Modal) { |
| 255 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
| 256 w1->Show(); |
| 257 |
| 258 // We only have one window, so it should use a solo header. |
| 259 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 260 |
| 261 // Create a fake modal window. |
| 262 scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); |
| 263 w2->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); |
| 264 w2->Show(); |
| 265 |
| 266 // Despite two windows, the first window should still be considered "solo" |
| 267 // because modal windows aren't included in the computation. |
| 268 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 269 } |
| 270 |
| 271 // Constrained windows should not use solo headers. |
| 272 TEST_F(SoloWindowTrackerTest, Constrained) { |
| 273 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
| 274 w1->Show(); |
| 275 |
| 276 // We only have one window, so it should use a solo header. |
| 277 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 278 |
| 279 // Create a fake constrained window. |
| 280 scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); |
| 281 w2->SetProperty(aura::client::kConstrainedWindowKey, true); |
| 282 w2->Show(); |
| 283 |
| 284 // Despite two windows, the first window should still be considered "solo" |
| 285 // because constrained windows aren't included in the computation. |
| 286 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 287 } |
| 288 |
| 289 // Non-drawing windows should not affect the solo computation. |
| 290 TEST_F(SoloWindowTrackerTest, NotDrawn) { |
| 291 aura::Window* w = CreateWindowInPrimary(); |
| 292 w->Show(); |
| 293 |
| 294 // We only have one window, so it should use a solo header. |
| 295 EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); |
| 296 |
| 297 // Create non-drawing window similar to DragDropTracker. |
| 298 aura::Window* not_drawn = new aura::Window(NULL); |
| 299 not_drawn->SetType(ui::wm::WINDOW_TYPE_NORMAL); |
| 300 not_drawn->Init(aura::WINDOW_LAYER_NOT_DRAWN); |
| 301 ParentWindowInPrimaryRootWindow(not_drawn); |
| 302 not_drawn->Show(); |
| 303 |
| 304 // Despite two windows, the first window should still be considered "solo" |
| 305 // because non-drawing windows aren't included in the computation. |
| 306 EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); |
| 307 } |
| 308 |
| 309 TEST_F(SoloWindowTrackerTest, MultiDisplay) { |
| 310 if (!SupportsMultipleDisplays()) |
| 311 return; |
| 312 |
| 313 UpdateDisplay("1000x600,600x400"); |
| 314 |
| 315 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
| 316 w1->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); |
| 317 w1->Show(); |
| 318 WindowRepaintChecker checker1(w1.get()); |
| 319 scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); |
| 320 w2->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); |
| 321 w2->Show(); |
| 322 WindowRepaintChecker checker2(w2.get()); |
| 323 |
| 324 // Now there are two windows in the same display, so we should not use solo |
| 325 // headers. |
| 326 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 327 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
| 328 |
| 329 // Moves the second window to the secondary display. Both w1/w2 should be |
| 330 // solo. |
| 331 w2->SetBoundsInScreen(gfx::Rect(1200, 0, 100, 100), |
| 332 ScreenAsh::GetSecondaryDisplay()); |
| 333 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 334 EXPECT_EQ(w2.get(), GetWindowWithSoloHeader(w2->GetRootWindow())); |
| 335 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
| 336 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); |
| 337 |
| 338 // Open two more windows in the primary display. |
| 339 scoped_ptr<aura::Window> w3(CreateWindowInPrimary()); |
| 340 w3->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); |
| 341 w3->Show(); |
| 342 scoped_ptr<aura::Window> w4(CreateWindowInPrimary()); |
| 343 w4->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); |
| 344 w4->Show(); |
| 345 |
| 346 // Because the primary display has three windows w1, w3, and w4, they |
| 347 // shouldn't be solo. w2 should be solo. |
| 348 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 349 EXPECT_EQ(w2.get(), GetWindowWithSoloHeader(w2->GetRootWindow())); |
| 350 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
| 351 |
| 352 // Move w4 to the secondary display. Now w2 shouldn't be solo anymore. |
| 353 w4->SetBoundsInScreen(gfx::Rect(1200, 0, 100, 100), GetSecondaryDisplay()); |
| 354 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 355 EXPECT_EQ(NULL, GetWindowWithSoloHeader(w2->GetRootWindow())); |
| 356 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); |
| 357 |
| 358 // Moves w3 to the secondary display too. Now w1 should be solo again. |
| 359 w3->SetBoundsInScreen(gfx::Rect(1200, 0, 100, 100), GetSecondaryDisplay()); |
| 360 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 361 EXPECT_EQ(NULL, GetWindowWithSoloHeader(w2->GetRootWindow())); |
| 362 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
| 363 |
| 364 // Change w3's state to maximize. Doesn't affect w1. |
| 365 wm::GetWindowState(w3.get())->Maximize(); |
| 366 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 367 EXPECT_EQ(NULL, GetWindowWithSoloHeader(w2->GetRootWindow())); |
| 368 |
| 369 // Close w3 and w4. |
| 370 w3.reset(); |
| 371 w4.reset(); |
| 372 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 373 EXPECT_EQ(w2.get(), GetWindowWithSoloHeader(w2->GetRootWindow())); |
| 374 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); |
| 375 |
| 376 // Move w2 back to the primary display. |
| 377 w2->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); |
| 378 EXPECT_EQ(w1->GetRootWindow(), w2->GetRootWindow()); |
| 379 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 380 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
| 381 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); |
| 382 |
| 383 // Close w2. |
| 384 w2.reset(); |
| 385 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
| 386 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
| 387 } |
| 388 |
| 389 TEST_F(SoloWindowTrackerTest, ChildWindowVisibility) { |
| 390 aura::Window* w = CreateWindowInPrimary(); |
| 391 w->Show(); |
| 392 |
| 393 // We only have one window, so it should use a solo header. |
| 394 EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); |
| 395 |
| 396 // Create a child window. This should not affect the solo-ness of |w1|. |
| 397 aura::Window* child = new aura::Window(NULL); |
| 398 child->SetType(ui::wm::WINDOW_TYPE_CONTROL); |
| 399 child->Init(aura::WINDOW_LAYER_TEXTURED); |
| 400 child->SetBounds(gfx::Rect(100, 100)); |
| 401 w->AddChild(child); |
| 402 child->Show(); |
| 403 EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); |
| 404 |
| 405 // Changing the visibility of |child| should not affect the solo-ness of |w1|. |
| 406 child->Hide(); |
| 407 EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); |
| 408 } |
| 409 |
| 410 TEST_F(SoloWindowTrackerTest, CreateAndDeleteSingleWindow) { |
| 411 // Ensure that creating/deleting a window works well and doesn't cause |
| 412 // crashes. See crbug.com/155634 |
| 413 scoped_ptr<aura::Window> w(CreateWindowInPrimary()); |
| 414 w->Show(); |
| 415 |
| 416 // We only have one window, so it should use a solo header. |
| 417 EXPECT_EQ(w.get(), GetWindowWithSoloHeaderInPrimary()); |
| 418 |
| 419 // Close the window. |
| 420 w.reset(); |
| 421 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
| 422 |
| 423 // Recreate another window again. |
| 424 w.reset(CreateWindowInPrimary()); |
| 425 w->Show(); |
| 426 EXPECT_EQ(w.get(), GetWindowWithSoloHeaderInPrimary()); |
| 427 } |
| 428 |
| 429 } // namespace ash |
OLD | NEW |