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/launcher/launcher.h" 6 #include "ash/root_window_controller.h" 7 #include "ash/screen_ash.h" 8 #include "ash/shelf/shelf_widget.h" 9 #include "ash/shell.h" 10 #include "ash/test/ash_test_base.h" 11 #include "ash/test/launcher_test_api.h" 12 #include "ash/test/shelf_view_test_api.h" 13 #include "ash/test/shell_test_api.h" 14 #include "ash/test/test_shelf_delegate.h" 15 #include "ash/wm/mru_window_tracker.h" 16 #include "ash/wm/overview/window_selector.h" 17 #include "ash/wm/overview/window_selector_controller.h" 18 #include "ash/wm/window_state.h" 19 #include "ash/wm/window_util.h" 20 #include "base/basictypes.h" 21 #include "base/compiler_specific.h" 22 #include "base/memory/scoped_vector.h" 23 #include "base/run_loop.h" 24 #include "ui/aura/client/activation_delegate.h" 25 #include "ui/aura/client/aura_constants.h" 26 #include "ui/aura/client/cursor_client.h" 27 #include "ui/aura/client/focus_client.h" 28 #include "ui/aura/root_window.h" 29 #include "ui/aura/test/event_generator.h" 30 #include "ui/aura/test/test_window_delegate.h" 31 #include "ui/aura/test/test_windows.h" 32 #include "ui/aura/window.h" 33 #include "ui/compositor/scoped_animation_duration_scale_mode.h" 34 #include "ui/gfx/rect_conversions.h" 35 #include "ui/gfx/transform.h" 36 37 namespace ash { 38 namespace internal { 39 40 namespace { 41 42 class NonActivatableActivationDelegate 43 : public aura::client::ActivationDelegate { 44 public: 45 virtual bool ShouldActivate() const OVERRIDE { 46 return false; 47 } 48 }; 49 50 bool IsWindowAbove(aura::Window* w1, aura::Window* w2) { 51 aura::Window* parent = w1->parent(); 52 DCHECK_EQ(parent, w2->parent()); 53 for (aura::Window::Windows::const_iterator iter = parent->children().begin(); 54 iter != parent->children().end(); ++iter) { 55 if (*iter == w1) 56 return false; 57 if (*iter == w2) 58 return true; 59 } 60 NOTREACHED(); 61 return false; 62 } 63 64 aura::Window* GetWindowByName(aura::Window* container, 65 const std::string& name) { 66 aura::Window* window = NULL; 67 for (aura::Window::Windows::const_iterator iter = 68 container->children().begin(); iter != container->children().end(); 69 ++iter) { 70 if ((*iter)->name() == name) { 71 // The name should be unique. 72 DCHECK(!window); 73 window = *iter; 74 } 75 } 76 return window; 77 } 78 79 // Returns the copy of |window| created for overview. It is found using the 80 // window name which should be the same as the source window's name with a 81 // special suffix, and in the same container as the source window. 82 aura::Window* GetCopyWindow(aura::Window* window) { 83 aura::Window* copy_window = NULL; 84 std::string copy_name = window->name() + " (Copy)"; 85 std::vector<aura::Window*> containers( 86 Shell::GetContainersFromAllRootWindows(window->parent()->id(), NULL)); 87 for (std::vector<aura::Window*>::iterator iter = containers.begin(); 88 iter != containers.end(); ++iter) { 89 aura::Window* found = GetWindowByName(*iter, copy_name); 90 if (found) { 91 // There should only be one copy window. 92 DCHECK(!copy_window); 93 copy_window = found; 94 } 95 } 96 return copy_window; 97 } 98 99 } // namespace 100 101 class WindowSelectorTest : public test::AshTestBase { 102 public: 103 WindowSelectorTest() {} 104 virtual ~WindowSelectorTest() {} 105 106 virtual void SetUp() OVERRIDE { 107 test::AshTestBase::SetUp(); 108 ASSERT_TRUE(test::TestShelfDelegate::instance()); 109 110 shelf_view_test_.reset(new test::ShelfViewTestAPI( 111 test::LauncherTestAPI(Launcher::ForPrimaryDisplay()).shelf_view())); 112 shelf_view_test_->SetAnimationDuration(1); 113 } 114 115 aura::Window* CreateWindow(const gfx::Rect& bounds) { 116 return CreateTestWindowInShellWithDelegate(&delegate_, -1, bounds); 117 } 118 119 aura::Window* CreateNonActivatableWindow(const gfx::Rect& bounds) { 120 aura::Window* window = CreateWindow(bounds); 121 aura::client::SetActivationDelegate(window, 122 &non_activatable_activation_delegate_); 123 EXPECT_FALSE(ash::wm::CanActivateWindow(window)); 124 return window; 125 } 126 127 aura::Window* CreatePanelWindow(const gfx::Rect& bounds) { 128 aura::Window* window = CreateTestWindowInShellWithDelegateAndType( 129 NULL, aura::client::WINDOW_TYPE_PANEL, 0, bounds); 130 test::TestShelfDelegate::instance()->AddLauncherItem(window); 131 shelf_view_test()->RunMessageLoopUntilAnimationsDone(); 132 return window; 133 } 134 135 bool WindowsOverlapping(aura::Window* window1, aura::Window* window2) { 136 gfx::RectF window1_bounds = GetTransformedTargetBounds(window1); 137 gfx::RectF window2_bounds = GetTransformedTargetBounds(window2); 138 return window1_bounds.Intersects(window2_bounds); 139 } 140 141 void ToggleOverview() { 142 ash::Shell::GetInstance()->window_selector_controller()->ToggleOverview(); 143 } 144 145 void Cycle(WindowSelector::Direction direction) { 146 ash::Shell::GetInstance()->window_selector_controller()-> 147 HandleCycleWindow(direction); 148 } 149 150 void StopCycling() { 151 ash::Shell::GetInstance()->window_selector_controller()->window_selector_-> 152 SelectWindow(); 153 } 154 155 void FireOverviewStartTimer() { 156 // Calls the method to start overview mode which is normally called by the 157 // timer. The timer will still fire and call this method triggering the 158 // DCHECK that overview mode was not already started, except that we call 159 // StopCycling before the timer has a chance to fire. 160 ash::Shell::GetInstance()->window_selector_controller()->window_selector_-> 161 StartOverview(); 162 } 163 164 gfx::Transform GetTransformRelativeTo(gfx::PointF origin, 165 const gfx::Transform& transform) { 166 gfx::Transform t; 167 t.Translate(origin.x(), origin.y()); 168 t.PreconcatTransform(transform); 169 t.Translate(-origin.x(), -origin.y()); 170 return t; 171 } 172 173 gfx::RectF GetTransformedBounds(aura::Window* window) { 174 gfx::RectF bounds(ash::ScreenAsh::ConvertRectToScreen( 175 window->parent(), window->layer()->bounds())); 176 gfx::Transform transform(GetTransformRelativeTo(bounds.origin(), 177 window->layer()->transform())); 178 transform.TransformRect(&bounds); 179 return bounds; 180 } 181 182 gfx::RectF GetTransformedTargetBounds(aura::Window* window) { 183 gfx::RectF bounds(ash::ScreenAsh::ConvertRectToScreen( 184 window->parent(), window->layer()->GetTargetBounds())); 185 gfx::Transform transform(GetTransformRelativeTo(bounds.origin(), 186 window->layer()->GetTargetTransform())); 187 transform.TransformRect(&bounds); 188 return bounds; 189 } 190 191 void ClickWindow(aura::Window* window) { 192 aura::test::EventGenerator event_generator(window->GetRootWindow(), window); 193 gfx::RectF target = GetTransformedBounds(window); 194 event_generator.ClickLeftButton(); 195 } 196 197 bool IsSelecting() { 198 return ash::Shell::GetInstance()->window_selector_controller()-> 199 IsSelecting(); 200 } 201 202 aura::Window* GetFocusedWindow() { 203 return aura::client::GetFocusClient( 204 Shell::GetPrimaryRootWindow())->GetFocusedWindow(); 205 } 206 207 test::ShelfViewTestAPI* shelf_view_test() { 208 return shelf_view_test_.get(); 209 } 210 211 private: 212 aura::test::TestWindowDelegate delegate_; 213 NonActivatableActivationDelegate non_activatable_activation_delegate_; 214 scoped_ptr<test::ShelfViewTestAPI> shelf_view_test_; 215 216 DISALLOW_COPY_AND_ASSIGN(WindowSelectorTest); 217 }; 218 219 // Tests entering overview mode with two windows and selecting one. 220 TEST_F(WindowSelectorTest, Basic) { 221 gfx::Rect bounds(0, 0, 400, 400); 222 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 223 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 224 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 225 scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); 226 scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds)); 227 EXPECT_TRUE(WindowsOverlapping(window1.get(), window2.get())); 228 EXPECT_TRUE(WindowsOverlapping(panel1.get(), panel2.get())); 229 wm::ActivateWindow(window2.get()); 230 EXPECT_FALSE(wm::IsActiveWindow(window1.get())); 231 EXPECT_TRUE(wm::IsActiveWindow(window2.get())); 232 EXPECT_EQ(window2.get(), GetFocusedWindow()); 233 // Hide the cursor before entering overview to test that it will be shown. 234 aura::client::GetCursorClient(root_window)->HideCursor(); 235 236 // In overview mode the windows should no longer overlap and focus should 237 // be removed from the window. 238 ToggleOverview(); 239 EXPECT_EQ(NULL, GetFocusedWindow()); 240 EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get())); 241 EXPECT_FALSE(WindowsOverlapping(window1.get(), panel1.get())); 242 // Panels 1 and 2 should still be overlapping being in a single selector 243 // item. 244 EXPECT_TRUE(WindowsOverlapping(panel1.get(), panel2.get())); 245 246 // The cursor should be visible and locked as a pointer 247 EXPECT_EQ(ui::kCursorPointer, 248 root_window->GetDispatcher()->last_cursor().native_type()); 249 EXPECT_TRUE(aura::client::GetCursorClient(root_window)->IsCursorLocked()); 250 EXPECT_TRUE(aura::client::GetCursorClient(root_window)->IsCursorVisible()); 251 252 // Clicking window 1 should activate it. 253 ClickWindow(window1.get()); 254 EXPECT_TRUE(wm::IsActiveWindow(window1.get())); 255 EXPECT_FALSE(wm::IsActiveWindow(window2.get())); 256 EXPECT_EQ(window1.get(), GetFocusedWindow()); 257 258 // Cursor should have been unlocked. 259 EXPECT_FALSE(aura::client::GetCursorClient(root_window)->IsCursorLocked()); 260 } 261 262 // Tests entering overview mode with two windows and selecting one. 263 TEST_F(WindowSelectorTest, FullscreenWindow) { 264 gfx::Rect bounds(0, 0, 400, 400); 265 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 266 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 267 scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); 268 wm::ActivateWindow(window1.get()); 269 270 wm::GetWindowState(window1.get())->ToggleFullscreen(); 271 // The panel is hidden in fullscreen mode. 272 EXPECT_FALSE(panel1->IsVisible()); 273 EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen()); 274 275 // Enter overview and select the fullscreen window. 276 ToggleOverview(); 277 278 // The panel becomes temporarily visible for the overview. 279 EXPECT_TRUE(panel1->IsVisible()); 280 ClickWindow(window1.get()); 281 282 // The window is still fullscreen as it was selected. The panel should again 283 // be hidden. 284 EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen()); 285 EXPECT_FALSE(panel1->IsVisible()); 286 287 // Entering overview and selecting another window, the previous window remains 288 // fullscreen. 289 // TODO(flackr): Currently the panel remains hidden, but should become visible 290 // again. 291 ToggleOverview(); 292 ClickWindow(window2.get()); 293 EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen()); 294 } 295 296 // Tests that the shelf dimming state is removed while in overview and restored 297 // on exiting overview. 298 TEST_F(WindowSelectorTest, OverviewUndimsShelf) { 299 gfx::Rect bounds(0, 0, 400, 400); 300 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 301 wm::WindowState* window_state = wm::GetWindowState(window1.get()); 302 window_state->Maximize(); 303 ash::ShelfWidget* shelf = Shell::GetPrimaryRootWindowController()->shelf(); 304 EXPECT_TRUE(shelf->GetDimsShelf()); 305 ToggleOverview(); 306 EXPECT_FALSE(shelf->GetDimsShelf()); 307 ToggleOverview(); 308 EXPECT_TRUE(shelf->GetDimsShelf()); 309 } 310 311 // Tests that beginning window selection hides the app list. 312 TEST_F(WindowSelectorTest, SelectingHidesAppList) { 313 gfx::Rect bounds(0, 0, 400, 400); 314 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 315 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 316 Shell::GetInstance()->ToggleAppList(NULL); 317 EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility()); 318 ToggleOverview(); 319 EXPECT_FALSE(Shell::GetInstance()->GetAppListTargetVisibility()); 320 ToggleOverview(); 321 322 // The app list uses an animation to fade out. If it is toggled on immediately 323 // after being removed the old widget is re-used and it does not gain focus. 324 // When running under normal circumstances this shouldn't be possible, but 325 // it is in a test without letting the message loop run. 326 RunAllPendingInMessageLoop(); 327 328 Shell::GetInstance()->ToggleAppList(NULL); 329 EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility()); 330 Cycle(WindowSelector::FORWARD); 331 EXPECT_FALSE(Shell::GetInstance()->GetAppListTargetVisibility()); 332 StopCycling(); 333 } 334 335 // Tests that a minimized window's visibility and layer visibility is correctly 336 // changed when entering overview and restored when leaving overview mode. 337 TEST_F(WindowSelectorTest, MinimizedWindowVisibility) { 338 gfx::Rect bounds(0, 0, 400, 400); 339 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 340 wm::WindowState* window_state = wm::GetWindowState(window1.get()); 341 window_state->Minimize(); 342 EXPECT_FALSE(window1->IsVisible()); 343 EXPECT_FALSE(window1->layer()->GetTargetVisibility()); 344 { 345 ui::ScopedAnimationDurationScaleMode normal_duration_mode( 346 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); 347 ToggleOverview(); 348 EXPECT_TRUE(window1->IsVisible()); 349 EXPECT_TRUE(window1->layer()->GetTargetVisibility()); 350 } 351 { 352 ui::ScopedAnimationDurationScaleMode normal_duration_mode( 353 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); 354 ToggleOverview(); 355 EXPECT_FALSE(window1->IsVisible()); 356 EXPECT_FALSE(window1->layer()->GetTargetVisibility()); 357 } 358 } 359 360 // Tests that a bounds change during overview is corrected for. 361 TEST_F(WindowSelectorTest, BoundsChangeDuringOverview) { 362 scoped_ptr<aura::Window> window(CreateWindow(gfx::Rect(0, 0, 400, 400))); 363 ToggleOverview(); 364 gfx::Rect overview_bounds = 365 ToEnclosingRect(GetTransformedTargetBounds(window.get())); 366 window->SetBounds(gfx::Rect(200, 0, 200, 200)); 367 gfx::Rect new_overview_bounds = 368 ToEnclosingRect(GetTransformedTargetBounds(window.get())); 369 EXPECT_EQ(overview_bounds.x(), new_overview_bounds.x()); 370 EXPECT_EQ(overview_bounds.y(), new_overview_bounds.y()); 371 EXPECT_EQ(overview_bounds.width(), new_overview_bounds.width()); 372 EXPECT_EQ(overview_bounds.height(), new_overview_bounds.height()); 373 ToggleOverview(); 374 } 375 376 // Tests entering overview mode with three windows and cycling through them. 377 TEST_F(WindowSelectorTest, BasicCycle) { 378 gfx::Rect bounds(0, 0, 400, 400); 379 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 380 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 381 scoped_ptr<aura::Window> window3(CreateWindow(bounds)); 382 wm::ActivateWindow(window3.get()); 383 wm::ActivateWindow(window2.get()); 384 wm::ActivateWindow(window1.get()); 385 EXPECT_TRUE(wm::IsActiveWindow(window1.get())); 386 EXPECT_FALSE(wm::IsActiveWindow(window2.get())); 387 EXPECT_FALSE(wm::IsActiveWindow(window3.get())); 388 389 Cycle(WindowSelector::FORWARD); 390 EXPECT_TRUE(IsSelecting()); 391 EXPECT_TRUE(wm::IsActiveWindow(window2.get())); 392 393 Cycle(WindowSelector::FORWARD); 394 EXPECT_TRUE(wm::IsActiveWindow(window3.get())); 395 396 StopCycling(); 397 EXPECT_FALSE(IsSelecting()); 398 EXPECT_FALSE(wm::IsActiveWindow(window1.get())); 399 EXPECT_FALSE(wm::IsActiveWindow(window2.get())); 400 EXPECT_TRUE(wm::IsActiveWindow(window3.get())); 401 } 402 403 // Tests that cycling through windows preserves the window stacking order. 404 TEST_F(WindowSelectorTest, CyclePreservesStackingOrder) { 405 gfx::Rect bounds(0, 0, 400, 400); 406 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 407 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 408 scoped_ptr<aura::Window> window3(CreateWindow(bounds)); 409 wm::ActivateWindow(window3.get()); 410 wm::ActivateWindow(window2.get()); 411 wm::ActivateWindow(window1.get()); 412 // Window order from top to bottom is 1, 2, 3. 413 EXPECT_TRUE(IsWindowAbove(window1.get(), window2.get())); 414 EXPECT_TRUE(IsWindowAbove(window2.get(), window3.get())); 415 416 // On window 2. 417 Cycle(WindowSelector::FORWARD); 418 EXPECT_TRUE(IsWindowAbove(window2.get(), window1.get())); 419 EXPECT_TRUE(IsWindowAbove(window1.get(), window3.get())); 420 421 // On window 3. 422 Cycle(WindowSelector::FORWARD); 423 EXPECT_TRUE(IsWindowAbove(window3.get(), window1.get())); 424 EXPECT_TRUE(IsWindowAbove(window1.get(), window2.get())); 425 426 // Back on window 1. 427 Cycle(WindowSelector::FORWARD); 428 EXPECT_TRUE(IsWindowAbove(window1.get(), window2.get())); 429 EXPECT_TRUE(IsWindowAbove(window2.get(), window3.get())); 430 StopCycling(); 431 } 432 433 // Tests that cycling through windows shows and minimizes windows as they 434 // are passed. 435 TEST_F(WindowSelectorTest, CyclePreservesMinimization) { 436 gfx::Rect bounds(0, 0, 400, 400); 437 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 438 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 439 wm::ActivateWindow(window2.get()); 440 wm::GetWindowState(window2.get())->Minimize(); 441 wm::ActivateWindow(window1.get()); 442 EXPECT_TRUE(wm::IsWindowMinimized(window2.get())); 443 444 // On window 2. 445 Cycle(WindowSelector::FORWARD); 446 EXPECT_FALSE(wm::IsWindowMinimized(window2.get())); 447 448 // Back on window 1. 449 Cycle(WindowSelector::FORWARD); 450 EXPECT_TRUE(wm::IsWindowMinimized(window2.get())); 451 452 StopCycling(); 453 EXPECT_TRUE(wm::IsWindowMinimized(window2.get())); 454 } 455 456 // Tests beginning cycling while in overview mode. 457 TEST_F(WindowSelectorTest, OverviewTransitionToCycle) { 458 gfx::Rect bounds(0, 0, 400, 400); 459 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 460 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 461 wm::ActivateWindow(window2.get()); 462 wm::ActivateWindow(window1.get()); 463 464 ToggleOverview(); 465 Cycle(WindowSelector::FORWARD); 466 StopCycling(); 467 468 EXPECT_TRUE(wm::IsActiveWindow(window2.get())); 469 EXPECT_FALSE(wm::IsActiveWindow(window1.get())); 470 EXPECT_EQ(window2.get(), GetFocusedWindow()); 471 } 472 473 // Tests cycles between panel and normal windows. 474 TEST_F(WindowSelectorTest, CyclePanels) { 475 gfx::Rect bounds(0, 0, 400, 400); 476 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 477 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 478 scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); 479 scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds)); 480 wm::ActivateWindow(window2.get()); 481 wm::ActivateWindow(window1.get()); 482 wm::ActivateWindow(panel2.get()); 483 wm::ActivateWindow(panel1.get()); 484 EXPECT_TRUE(wm::IsActiveWindow(panel1.get())); 485 486 // Cycling once should select window1 since the panels are grouped into a 487 // single selectable item. 488 Cycle(WindowSelector::FORWARD); 489 StopCycling(); 490 EXPECT_TRUE(wm::IsActiveWindow(window1.get())); 491 492 // Cycling again should select the most recently used panel. 493 Cycle(WindowSelector::FORWARD); 494 StopCycling(); 495 EXPECT_TRUE(wm::IsActiveWindow(panel1.get())); 496 } 497 498 // Tests the visibility of panel windows during cycling. 499 TEST_F(WindowSelectorTest, CyclePanelVisibility) { 500 gfx::Rect bounds(0, 0, 400, 400); 501 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 502 scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); 503 wm::ActivateWindow(panel1.get()); 504 wm::ActivateWindow(window1.get()); 505 506 Cycle(WindowSelector::FORWARD); 507 FireOverviewStartTimer(); 508 EXPECT_EQ(1.0f, panel1->layer()->GetTargetOpacity()); 509 StopCycling(); 510 } 511 512 // Tests cycles between panel and normal windows. 513 TEST_F(WindowSelectorTest, CyclePanelsDestroyed) { 514 gfx::Rect bounds(0, 0, 400, 400); 515 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 516 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 517 scoped_ptr<aura::Window> window3(CreateWindow(bounds)); 518 scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); 519 scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds)); 520 wm::ActivateWindow(window3.get()); 521 wm::ActivateWindow(panel2.get()); 522 wm::ActivateWindow(panel1.get()); 523 wm::ActivateWindow(window2.get()); 524 wm::ActivateWindow(window1.get()); 525 EXPECT_TRUE(wm::IsActiveWindow(window1.get())); 526 527 // Cycling once highlights window2. 528 Cycle(WindowSelector::FORWARD); 529 // All panels are destroyed. 530 panel1.reset(); 531 panel2.reset(); 532 // Cycling again should now select window3. 533 Cycle(WindowSelector::FORWARD); 534 StopCycling(); 535 EXPECT_TRUE(wm::IsActiveWindow(window3.get())); 536 } 537 538 // Tests cycles between panel and normal windows. 539 TEST_F(WindowSelectorTest, CycleMruPanelDestroyed) { 540 gfx::Rect bounds(0, 0, 400, 400); 541 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 542 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 543 scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); 544 scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds)); 545 wm::ActivateWindow(panel2.get()); 546 wm::ActivateWindow(panel1.get()); 547 wm::ActivateWindow(window2.get()); 548 wm::ActivateWindow(window1.get()); 549 EXPECT_TRUE(wm::IsActiveWindow(window1.get())); 550 551 // Cycling once highlights window2. 552 Cycle(WindowSelector::FORWARD); 553 // Panel 1 is the next item as the MRU panel, removing it should make panel 2 554 // the next window to be selected. 555 panel1.reset(); 556 // Cycling again should now select window3. 557 Cycle(WindowSelector::FORWARD); 558 StopCycling(); 559 EXPECT_TRUE(wm::IsActiveWindow(panel2.get())); 560 } 561 562 // Tests that a newly created window aborts overview. 563 TEST_F(WindowSelectorTest, NewWindowCancelsOveriew) { 564 gfx::Rect bounds(0, 0, 400, 400); 565 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 566 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 567 ToggleOverview(); 568 EXPECT_TRUE(IsSelecting()); 569 570 // A window being created should exit overview mode. 571 scoped_ptr<aura::Window> window3(CreateWindow(bounds)); 572 EXPECT_FALSE(IsSelecting()); 573 } 574 575 // Tests that a window activation exits overview mode. 576 TEST_F(WindowSelectorTest, ActivationCancelsOveriew) { 577 gfx::Rect bounds(0, 0, 400, 400); 578 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 579 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 580 window2->Focus(); 581 ToggleOverview(); 582 EXPECT_TRUE(IsSelecting()); 583 584 // A window being activated should exit overview mode. 585 window1->Focus(); 586 EXPECT_FALSE(IsSelecting()); 587 588 // window1 should be focused after exiting even though window2 was focused on 589 // entering overview because we exited due to an activation. 590 EXPECT_EQ(window1.get(), GetFocusedWindow()); 591 } 592 593 // Verifies that overview mode only begins after a delay when cycling. 594 TEST_F(WindowSelectorTest, CycleOverviewDelay) { 595 gfx::Rect bounds(0, 0, 400, 400); 596 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 597 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 598 EXPECT_TRUE(WindowsOverlapping(window1.get(), window2.get())); 599 600 // When cycling first starts, the windows will still be overlapping. 601 Cycle(WindowSelector::FORWARD); 602 EXPECT_TRUE(IsSelecting()); 603 EXPECT_TRUE(WindowsOverlapping(window1.get(), window2.get())); 604 605 // Once the overview timer fires, the windows should no longer overlap. 606 FireOverviewStartTimer(); 607 EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get())); 608 StopCycling(); 609 } 610 611 // Tests that exiting overview mode without selecting a window restores focus 612 // to the previously focused window. 613 TEST_F(WindowSelectorTest, CancelRestoresFocus) { 614 gfx::Rect bounds(0, 0, 400, 400); 615 scoped_ptr<aura::Window> window(CreateWindow(bounds)); 616 wm::ActivateWindow(window.get()); 617 EXPECT_EQ(window.get(), GetFocusedWindow()); 618 619 // In overview mode, focus should be removed. 620 ToggleOverview(); 621 EXPECT_EQ(NULL, GetFocusedWindow()); 622 623 // If canceling overview mode, focus should be restored. 624 ToggleOverview(); 625 EXPECT_EQ(window.get(), GetFocusedWindow()); 626 } 627 628 // Tests that overview mode is exited if the last remaining window is destroyed. 629 TEST_F(WindowSelectorTest, LastWindowDestroyed) { 630 gfx::Rect bounds(0, 0, 400, 400); 631 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 632 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 633 ToggleOverview(); 634 635 window1.reset(); 636 window2.reset(); 637 EXPECT_FALSE(IsSelecting()); 638 } 639 640 // Tests that entering overview mode restores a window to its original 641 // target location. 642 TEST_F(WindowSelectorTest, QuickReentryRestoresInitialTransform) { 643 gfx::Rect bounds(0, 0, 400, 400); 644 scoped_ptr<aura::Window> window(CreateWindow(bounds)); 645 gfx::Rect initial_bounds = ToEnclosingRect( 646 GetTransformedBounds(window.get())); 647 ToggleOverview(); 648 // Quickly exit and reenter overview mode. The window should still be 649 // animating when we reenter. We cannot short circuit animations for this but 650 // we also don't have to wait for them to complete. 651 { 652 ui::ScopedAnimationDurationScaleMode normal_duration_mode( 653 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); 654 ToggleOverview(); 655 ToggleOverview(); 656 } 657 EXPECT_NE(initial_bounds, ToEnclosingRect( 658 GetTransformedTargetBounds(window.get()))); 659 ToggleOverview(); 660 EXPECT_FALSE(IsSelecting()); 661 EXPECT_EQ(initial_bounds, ToEnclosingRect( 662 GetTransformedTargetBounds(window.get()))); 663 } 664 665 // Tests that non-activatable windows are hidden when entering overview mode. 666 TEST_F(WindowSelectorTest, NonActivatableWindowsHidden) { 667 gfx::Rect bounds(0, 0, 400, 400); 668 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 669 scoped_ptr<aura::Window> window2(CreateWindow(bounds)); 670 scoped_ptr<aura::Window> non_activatable_window( 671 CreateNonActivatableWindow(Shell::GetPrimaryRootWindow()->bounds())); 672 EXPECT_TRUE(non_activatable_window->IsVisible()); 673 ToggleOverview(); 674 EXPECT_FALSE(non_activatable_window->IsVisible()); 675 ToggleOverview(); 676 EXPECT_TRUE(non_activatable_window->IsVisible()); 677 678 // Test that a window behind the fullscreen non-activatable window can be 679 // clicked. 680 non_activatable_window->parent()->StackChildAtTop( 681 non_activatable_window.get()); 682 ToggleOverview(); 683 ClickWindow(window1.get()); 684 EXPECT_FALSE(IsSelecting()); 685 EXPECT_TRUE(wm::IsActiveWindow(window1.get())); 686 } 687 688 // Tests that windows with modal child windows are transformed with the modal 689 // child even though not activatable themselves. 690 TEST_F(WindowSelectorTest, ModalChild) { 691 gfx::Rect bounds(0, 0, 400, 400); 692 scoped_ptr<aura::Window> window1(CreateWindow(bounds)); 693 scoped_ptr<aura::Window> child1(CreateWindow(bounds)); 694 child1->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); 695 window1->AddTransientChild(child1.get()); 696 EXPECT_EQ(window1->parent(), child1->parent()); 697 ToggleOverview(); 698 EXPECT_TRUE(window1->IsVisible()); 699 EXPECT_TRUE(child1->IsVisible()); 700 EXPECT_EQ(ToEnclosingRect(GetTransformedTargetBounds(child1.get())), 701 ToEnclosingRect(GetTransformedTargetBounds(window1.get()))); 702 ToggleOverview(); 703 } 704 705 // Tests that clicking a modal window's parent activates the modal window in 706 // overview. 707 TEST_F(WindowSelectorTest, ClickModalWindowParent) { 708 scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(0, 0, 180, 180))); 709 scoped_ptr<aura::Window> child1(CreateWindow(gfx::Rect(200, 0, 180, 180))); 710 child1->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); 711 window1->AddTransientChild(child1.get()); 712 EXPECT_FALSE(WindowsOverlapping(window1.get(), child1.get())); 713 EXPECT_EQ(window1->parent(), child1->parent()); 714 ToggleOverview(); 715 // Given that their relative positions are preserved, the windows should still 716 // not overlap. 717 EXPECT_FALSE(WindowsOverlapping(window1.get(), child1.get())); 718 ClickWindow(window1.get()); 719 EXPECT_FALSE(IsSelecting()); 720 721 // Clicking on window1 should activate child1. 722 EXPECT_TRUE(wm::IsActiveWindow(child1.get())); 723 } 724 725 // Tests that windows remain on the display they are currently on in overview 726 // mode. 727 TEST_F(WindowSelectorTest, MultipleDisplays) { 728 if (!SupportsMultipleDisplays()) 729 return; 730 731 UpdateDisplay("600x400,600x400"); 732 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 733 gfx::Rect bounds1(0, 0, 400, 400); 734 gfx::Rect bounds2(650, 0, 400, 400); 735 736 scoped_ptr<aura::Window> window1(CreateWindow(bounds1)); 737 scoped_ptr<aura::Window> window2(CreateWindow(bounds1)); 738 scoped_ptr<aura::Window> window3(CreateWindow(bounds2)); 739 scoped_ptr<aura::Window> window4(CreateWindow(bounds2)); 740 scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds1)); 741 scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds1)); 742 scoped_ptr<aura::Window> panel3(CreatePanelWindow(bounds2)); 743 scoped_ptr<aura::Window> panel4(CreatePanelWindow(bounds2)); 744 EXPECT_EQ(root_windows[0], window1->GetRootWindow()); 745 EXPECT_EQ(root_windows[0], window2->GetRootWindow()); 746 EXPECT_EQ(root_windows[1], window3->GetRootWindow()); 747 EXPECT_EQ(root_windows[1], window4->GetRootWindow()); 748 749 EXPECT_EQ(root_windows[0], panel1->GetRootWindow()); 750 EXPECT_EQ(root_windows[0], panel2->GetRootWindow()); 751 EXPECT_EQ(root_windows[1], panel3->GetRootWindow()); 752 EXPECT_EQ(root_windows[1], panel4->GetRootWindow()); 753 754 // In overview mode, each window remains in the same root window. 755 ToggleOverview(); 756 EXPECT_EQ(root_windows[0], window1->GetRootWindow()); 757 EXPECT_EQ(root_windows[0], window2->GetRootWindow()); 758 EXPECT_EQ(root_windows[1], window3->GetRootWindow()); 759 EXPECT_EQ(root_windows[1], window4->GetRootWindow()); 760 EXPECT_EQ(root_windows[0], panel1->GetRootWindow()); 761 EXPECT_EQ(root_windows[0], panel2->GetRootWindow()); 762 EXPECT_EQ(root_windows[1], panel3->GetRootWindow()); 763 EXPECT_EQ(root_windows[1], panel4->GetRootWindow()); 764 765 EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains( 766 ToEnclosingRect(GetTransformedTargetBounds(window1.get())))); 767 EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains( 768 ToEnclosingRect(GetTransformedTargetBounds(window2.get())))); 769 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 770 ToEnclosingRect(GetTransformedTargetBounds(window3.get())))); 771 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 772 ToEnclosingRect(GetTransformedTargetBounds(window4.get())))); 773 774 EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains( 775 ToEnclosingRect(GetTransformedTargetBounds(panel1.get())))); 776 EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains( 777 ToEnclosingRect(GetTransformedTargetBounds(panel2.get())))); 778 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 779 ToEnclosingRect(GetTransformedTargetBounds(panel3.get())))); 780 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 781 ToEnclosingRect(GetTransformedTargetBounds(panel4.get())))); 782 EXPECT_TRUE(WindowsOverlapping(panel1.get(), panel2.get())); 783 EXPECT_TRUE(WindowsOverlapping(panel3.get(), panel4.get())); 784 EXPECT_FALSE(WindowsOverlapping(panel1.get(), panel3.get())); 785 } 786 787 // Verifies that the single display overview used during alt tab cycling uses 788 // the display of the selected window by default. 789 TEST_F(WindowSelectorTest, CycleOverviewUsesCurrentDisplay) { 790 if (!SupportsMultipleDisplays()) 791 return; 792 793 UpdateDisplay("400x400,400x400"); 794 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 795 796 scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(0, 0, 100, 100))); 797 scoped_ptr<aura::Window> window2(CreateWindow(gfx::Rect(450, 0, 100, 100))); 798 EXPECT_EQ(root_windows[0], window1->GetRootWindow()); 799 EXPECT_EQ(root_windows[1], window2->GetRootWindow()); 800 wm::ActivateWindow(window2.get()); 801 wm::ActivateWindow(window1.get()); 802 EXPECT_EQ(root_windows[0], Shell::GetTargetRootWindow()); 803 804 Cycle(WindowSelector::FORWARD); 805 FireOverviewStartTimer(); 806 807 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 808 ToEnclosingRect(GetTransformedTargetBounds(window1.get())))); 809 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 810 ToEnclosingRect(GetTransformedTargetBounds(window2.get())))); 811 StopCycling(); 812 } 813 814 // Verifies that the windows being shown on another display are copied. 815 TEST_F(WindowSelectorTest, CycleMultipleDisplaysCopiesWindows) { 816 if (!SupportsMultipleDisplays()) 817 return; 818 819 UpdateDisplay("400x400,400x400"); 820 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 821 822 gfx::Rect root1_rect(0, 0, 100, 100); 823 gfx::Rect root2_rect(450, 0, 100, 100); 824 scoped_ptr<aura::Window> unmoved1(CreateWindow(root2_rect)); 825 scoped_ptr<aura::Window> unmoved2(CreateWindow(root2_rect)); 826 scoped_ptr<aura::Window> moved1_trans_parent(CreateWindow(root1_rect)); 827 scoped_ptr<aura::Window> moved1(CreateWindow(root1_rect)); 828 unmoved1->SetName("unmoved1"); 829 unmoved2->SetName("unmoved2"); 830 moved1->SetName("moved1"); 831 moved1->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); 832 moved1_trans_parent->AddTransientChild(moved1.get()); 833 moved1_trans_parent->SetName("moved1_trans_parent"); 834 835 EXPECT_EQ(root_windows[0], moved1->GetRootWindow()); 836 EXPECT_EQ(root_windows[0], moved1_trans_parent->GetRootWindow()); 837 EXPECT_EQ(root_windows[1], unmoved1->GetRootWindow()); 838 EXPECT_EQ(root_windows[1], unmoved2->GetRootWindow()); 839 wm::ActivateWindow(unmoved2.get()); 840 wm::ActivateWindow(unmoved1.get()); 841 842 Cycle(WindowSelector::FORWARD); 843 FireOverviewStartTimer(); 844 845 // All windows are moved to second root window. 846 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 847 ToEnclosingRect(GetTransformedTargetBounds(unmoved1.get())))); 848 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 849 ToEnclosingRect(GetTransformedTargetBounds(unmoved2.get())))); 850 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 851 ToEnclosingRect(GetTransformedTargetBounds(moved1.get())))); 852 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 853 ToEnclosingRect(GetTransformedTargetBounds(moved1_trans_parent.get())))); 854 855 // unmoved1 and unmoved2 were already on the correct display and should not 856 // have been copied. 857 EXPECT_TRUE(!GetCopyWindow(unmoved1.get())); 858 EXPECT_TRUE(!GetCopyWindow(unmoved2.get())); 859 860 // moved1 and its transient parent moved1_trans_parent should have also been 861 // copied for displaying on root_windows[1]. 862 aura::Window* copy1 = GetCopyWindow(moved1.get()); 863 aura::Window* copy1_trans_parent = GetCopyWindow(moved1_trans_parent.get()); 864 ASSERT_FALSE(!copy1); 865 ASSERT_FALSE(!copy1_trans_parent); 866 867 // Verify that the bounds and transform of the copy match the original window 868 // but that it is on the other root window. 869 EXPECT_EQ(root_windows[1], copy1->GetRootWindow()); 870 EXPECT_EQ(moved1->GetBoundsInScreen().ToString(), 871 copy1->GetBoundsInScreen().ToString()); 872 EXPECT_EQ(moved1->layer()->GetTargetTransform().ToString(), 873 copy1->layer()->GetTargetTransform().ToString()); 874 StopCycling(); 875 876 // After cycling the copy windows should have been destroyed. 877 RunAllPendingInMessageLoop(); 878 EXPECT_TRUE(!GetCopyWindow(moved1.get())); 879 EXPECT_TRUE(!GetCopyWindow(moved1_trans_parent.get())); 880 } 881 882 // Tests that beginning to cycle from overview mode moves windows to the 883 // active display. 884 TEST_F(WindowSelectorTest, MultipleDisplaysOverviewTransitionToCycle) { 885 if (!SupportsMultipleDisplays()) 886 return; 887 888 UpdateDisplay("400x400,400x400"); 889 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 890 891 scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(0, 0, 100, 100))); 892 scoped_ptr<aura::Window> window2(CreateWindow(gfx::Rect(450, 0, 100, 100))); 893 EXPECT_EQ(root_windows[0], window1->GetRootWindow()); 894 EXPECT_EQ(root_windows[1], window2->GetRootWindow()); 895 wm::ActivateWindow(window2.get()); 896 wm::ActivateWindow(window1.get()); 897 898 ToggleOverview(); 899 EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains( 900 ToEnclosingRect(GetTransformedTargetBounds(window1.get())))); 901 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains( 902 ToEnclosingRect(GetTransformedTargetBounds(window2.get())))); 903 904 Cycle(WindowSelector::FORWARD); 905 EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains( 906 ToEnclosingRect(GetTransformedTargetBounds(window1.get())))); 907 EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains( 908 ToEnclosingRect(GetTransformedTargetBounds(window2.get())))); 909 StopCycling(); 910 } 911 912 // Tests that a bounds change during overview is corrected for. 913 TEST_F(WindowSelectorTest, BoundsChangeDuringCycleOnOtherDisplay) { 914 if (!SupportsMultipleDisplays()) 915 return; 916 917 UpdateDisplay("400x400,400x400"); 918 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 919 920 scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(0, 0, 100, 100))); 921 scoped_ptr<aura::Window> window2(CreateWindow(gfx::Rect(450, 0, 100, 100))); 922 scoped_ptr<aura::Window> window3(CreateWindow(gfx::Rect(450, 0, 100, 100))); 923 EXPECT_EQ(root_windows[0], window1->GetRootWindow()); 924 EXPECT_EQ(root_windows[1], window2->GetRootWindow()); 925 EXPECT_EQ(root_windows[1], window3->GetRootWindow()); 926 wm::ActivateWindow(window1.get()); 927 wm::ActivateWindow(window2.get()); 928 wm::ActivateWindow(window3.get()); 929 930 Cycle(WindowSelector::FORWARD); 931 FireOverviewStartTimer(); 932 933 gfx::Rect overview_bounds( 934 ToEnclosingRect(GetTransformedTargetBounds(window1.get()))); 935 EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains(overview_bounds)); 936 937 // Change the position and size of window1 (being displayed on the second 938 // root window) and it should remain within the same bounds. 939 window1->SetBounds(gfx::Rect(100, 0, 200, 200)); 940 gfx::Rect new_overview_bounds = 941 ToEnclosingRect(GetTransformedTargetBounds(window1.get())); 942 EXPECT_EQ(overview_bounds.x(), new_overview_bounds.x()); 943 EXPECT_EQ(overview_bounds.y(), new_overview_bounds.y()); 944 EXPECT_EQ(overview_bounds.width(), new_overview_bounds.width()); 945 EXPECT_EQ(overview_bounds.height(), new_overview_bounds.height()); 946 StopCycling(); 947 } 948 949 // Tests shutting down during overview. 950 TEST_F(WindowSelectorTest, Shutdown) { 951 gfx::Rect bounds(0, 0, 400, 400); 952 // These windows will be deleted when the test exits and the Shell instance 953 // is shut down. 954 aura::Window* window1(CreateWindow(bounds)); 955 aura::Window* window2(CreateWindow(bounds)); 956 aura::Window* window3(CreatePanelWindow(bounds)); 957 aura::Window* window4(CreatePanelWindow(bounds)); 958 959 wm::ActivateWindow(window4); 960 wm::ActivateWindow(window3); 961 wm::ActivateWindow(window2); 962 wm::ActivateWindow(window1); 963 964 ToggleOverview(); 965 } 966 967 // Tests removing a display during overview. 968 TEST_F(WindowSelectorTest, RemoveDisplay) { 969 if (!SupportsMultipleDisplays()) 970 return; 971 972 UpdateDisplay("400x400,400x400"); 973 gfx::Rect bounds1(0, 0, 100, 100); 974 gfx::Rect bounds2(450, 0, 100, 100); 975 scoped_ptr<aura::Window> window1(CreateWindow(bounds1)); 976 scoped_ptr<aura::Window> window2(CreateWindow(bounds2)); 977 scoped_ptr<aura::Window> window3(CreatePanelWindow(bounds1)); 978 scoped_ptr<aura::Window> window4(CreatePanelWindow(bounds2)); 979 980 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 981 EXPECT_EQ(root_windows[0], window1->GetRootWindow()); 982 EXPECT_EQ(root_windows[1], window2->GetRootWindow()); 983 EXPECT_EQ(root_windows[0], window3->GetRootWindow()); 984 EXPECT_EQ(root_windows[1], window4->GetRootWindow()); 985 986 wm::ActivateWindow(window4.get()); 987 wm::ActivateWindow(window3.get()); 988 wm::ActivateWindow(window2.get()); 989 wm::ActivateWindow(window1.get()); 990 991 ToggleOverview(); 992 EXPECT_TRUE(IsSelecting()); 993 UpdateDisplay("400x400"); 994 EXPECT_FALSE(IsSelecting()); 995 } 996 997 } // namespace internal 998 } // namespace ash 999