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/immersive_fullscreen_controller.h" 6 7 #include "ash/display/display_manager.h" 8 #include "ash/root_window_controller.h" 9 #include "ash/screen_ash.h" 10 #include "ash/shelf/shelf_layout_manager.h" 11 #include "ash/shelf/shelf_types.h" 12 #include "ash/shell.h" 13 #include "ash/test/ash_test_base.h" 14 #include "ui/aura/client/aura_constants.h" 15 #include "ui/aura/client/cursor_client.h" 16 #include "ui/aura/env.h" 17 #include "ui/aura/root_window.h" 18 #include "ui/aura/test/event_generator.h" 19 #include "ui/aura/window.h" 20 #include "ui/gfx/animation/slide_animation.h" 21 #include "ui/views/bubble/bubble_delegate.h" 22 #include "ui/views/view.h" 23 #include "ui/views/widget/widget.h" 24 25 // For now, immersive fullscreen is Chrome OS only. 26 #if defined(OS_CHROMEOS) 27 28 namespace ash { 29 30 namespace { 31 32 class MockImmersiveFullscreenControllerDelegate 33 : public ImmersiveFullscreenController::Delegate { 34 public: 35 MockImmersiveFullscreenControllerDelegate(views::View* top_container_view) 36 : top_container_view_(top_container_view), 37 enabled_(false), 38 visible_fraction_(1) { 39 } 40 virtual ~MockImmersiveFullscreenControllerDelegate() {} 41 42 // ImmersiveFullscreenController::Delegate overrides: 43 virtual void OnImmersiveRevealStarted() OVERRIDE { 44 enabled_ = true; 45 visible_fraction_ = 0; 46 } 47 virtual void OnImmersiveRevealEnded() OVERRIDE { 48 visible_fraction_ = 0; 49 } 50 virtual void OnImmersiveFullscreenExited() OVERRIDE { 51 enabled_ = false; 52 visible_fraction_ = 1; 53 } 54 virtual void SetVisibleFraction(double visible_fraction) OVERRIDE { 55 visible_fraction_ = visible_fraction; 56 } 57 virtual std::vector<gfx::Rect> GetVisibleBoundsInScreen() const OVERRIDE { 58 std::vector<gfx::Rect> bounds_in_screen; 59 bounds_in_screen.push_back(top_container_view_->GetBoundsInScreen()); 60 return bounds_in_screen; 61 } 62 63 bool is_enabled() const { 64 return enabled_; 65 } 66 67 double visible_fraction() const { 68 return visible_fraction_; 69 } 70 71 private: 72 views::View* top_container_view_; 73 bool enabled_; 74 double visible_fraction_; 75 76 DISALLOW_COPY_AND_ASSIGN(MockImmersiveFullscreenControllerDelegate); 77 }; 78 79 } // namespace 80 81 ///////////////////////////////////////////////////////////////////////////// 82 83 class ImmersiveFullscreenControllerTest : public ash::test::AshTestBase { 84 public: 85 enum Modality { 86 MODALITY_MOUSE, 87 MODALITY_TOUCH, 88 MODALITY_GESTURE 89 }; 90 91 ImmersiveFullscreenControllerTest() : widget_(NULL), top_container_(NULL) {} 92 virtual ~ImmersiveFullscreenControllerTest() {} 93 94 ImmersiveFullscreenController* controller() { 95 return controller_.get(); 96 } 97 98 views::View* top_container() { 99 return top_container_; 100 } 101 102 aura::Window* window() { 103 return widget_->GetNativeWindow(); 104 } 105 106 MockImmersiveFullscreenControllerDelegate* delegate() { 107 return delegate_.get(); 108 } 109 110 // Access to private data from the controller. 111 bool top_edge_hover_timer_running() const { 112 return controller_->top_edge_hover_timer_.IsRunning(); 113 } 114 int mouse_x_when_hit_top() const { 115 return controller_->mouse_x_when_hit_top_in_screen_; 116 } 117 118 // ash::test::AshTestBase overrides: 119 virtual void SetUp() OVERRIDE { 120 ash::test::AshTestBase::SetUp(); 121 122 widget_ = new views::Widget(); 123 views::Widget::InitParams params; 124 params.context = CurrentContext(); 125 widget_->Init(params); 126 widget_->Show(); 127 128 window()->SetProperty(aura::client::kShowStateKey, 129 ui::SHOW_STATE_FULLSCREEN); 130 131 top_container_ = new views::View(); 132 top_container_->SetBounds( 133 0, 0, widget_->GetWindowBoundsInScreen().width(), 100); 134 top_container_->SetFocusable(true); 135 widget_->GetContentsView()->AddChildView(top_container_); 136 137 delegate_.reset( 138 new MockImmersiveFullscreenControllerDelegate(top_container_)); 139 controller_.reset(new ImmersiveFullscreenController); 140 controller_->Init(delegate_.get(), widget_, top_container_); 141 controller_->SetupForTest(); 142 143 // The mouse is moved so that it is not over |top_container_| by 144 // AshTestBase. 145 } 146 147 // Enables / disables immersive fullscreen. 148 void SetEnabled(bool enabled) { 149 controller_->SetEnabled(ImmersiveFullscreenController::WINDOW_TYPE_OTHER, 150 enabled); 151 } 152 153 // Attempt to reveal the top-of-window views via |modality|. 154 // The top-of-window views can only be revealed via mouse hover or a gesture. 155 void AttemptReveal(Modality modality) { 156 ASSERT_NE(modality, MODALITY_TOUCH); 157 AttemptRevealStateChange(true, modality); 158 } 159 160 // Attempt to unreveal the top-of-window views via |modality|. The 161 // top-of-window views can be unrevealed via any modality. 162 void AttemptUnreveal(Modality modality) { 163 AttemptRevealStateChange(false, modality); 164 } 165 166 // Sets whether the mouse is hovered above |top_container_|. 167 // SetHovered(true) moves the mouse over the |top_container_| but does not 168 // move it to the top of the screen so will not initiate a reveal. 169 void SetHovered(bool is_mouse_hovered) { 170 MoveMouse(0, is_mouse_hovered ? 10 : top_container_->height() + 100); 171 } 172 173 // Move the mouse to the given coordinates. The coordinates should be in 174 // |top_container_| coordinates. 175 void MoveMouse(int x, int y) { 176 gfx::Point screen_position(x, y); 177 views::View::ConvertPointToScreen(top_container_, &screen_position); 178 GetEventGenerator().MoveMouseTo(screen_position.x(), screen_position.y()); 179 180 // If the top edge timer started running as a result of the mouse move, run 181 // the task which occurs after the timer delay. This reveals the 182 // top-of-window views synchronously if the mouse is hovered at the top of 183 // the screen. 184 if (controller()->top_edge_hover_timer_.IsRunning()) { 185 controller()->top_edge_hover_timer_.user_task().Run(); 186 controller()->top_edge_hover_timer_.Stop(); 187 } 188 } 189 190 private: 191 // Attempt to change the revealed state to |revealed| via |modality|. 192 void AttemptRevealStateChange(bool revealed, Modality modality) { 193 // Compute the event position in |top_container_| coordinates. 194 gfx::Point event_position(0, revealed ? 0 : top_container_->height() + 100); 195 switch (modality) { 196 case MODALITY_MOUSE: { 197 MoveMouse(event_position.x(), event_position.y()); 198 break; 199 } 200 case MODALITY_TOUCH: { 201 gfx::Point screen_position = event_position; 202 views::View::ConvertPointToScreen(top_container_, &screen_position); 203 204 aura::test::EventGenerator& event_generator(GetEventGenerator()); 205 event_generator.MoveTouch(event_position); 206 event_generator.PressTouch(); 207 event_generator.ReleaseTouch(); 208 break; 209 } 210 case MODALITY_GESTURE: { 211 aura::client::GetCursorClient(CurrentContext())->DisableMouseEvents(); 212 ImmersiveFullscreenController::SwipeType swipe_type = revealed ? 213 ImmersiveFullscreenController::SWIPE_OPEN : 214 ImmersiveFullscreenController::SWIPE_CLOSE; 215 controller_->UpdateRevealedLocksForSwipe(swipe_type); 216 break; 217 } 218 } 219 } 220 221 scoped_ptr<ImmersiveFullscreenController> controller_; 222 scoped_ptr<MockImmersiveFullscreenControllerDelegate> delegate_; 223 views::Widget* widget_; // Owned by the native widget. 224 views::View* top_container_; // Owned by |root_view_|. 225 226 DISALLOW_COPY_AND_ASSIGN(ImmersiveFullscreenControllerTest); 227 }; 228 229 // Test the initial state and that the delegate gets notified of the 230 // top-of-window views getting hidden and revealed. 231 TEST_F(ImmersiveFullscreenControllerTest, Delegate) { 232 // Initial state. 233 EXPECT_FALSE(controller()->IsEnabled()); 234 EXPECT_FALSE(controller()->IsRevealed()); 235 EXPECT_FALSE(delegate()->is_enabled()); 236 237 // Enabling initially hides the top views. 238 SetEnabled(true); 239 EXPECT_TRUE(controller()->IsEnabled()); 240 EXPECT_FALSE(controller()->IsRevealed()); 241 EXPECT_TRUE(delegate()->is_enabled()); 242 EXPECT_EQ(0, delegate()->visible_fraction()); 243 244 // Revealing shows the top views. 245 AttemptReveal(MODALITY_MOUSE); 246 EXPECT_TRUE(controller()->IsEnabled()); 247 EXPECT_TRUE(controller()->IsRevealed()); 248 EXPECT_TRUE(delegate()->is_enabled()); 249 EXPECT_EQ(1, delegate()->visible_fraction()); 250 251 // Disabling ends the immersive reveal. 252 SetEnabled(false); 253 EXPECT_FALSE(controller()->IsEnabled()); 254 EXPECT_FALSE(controller()->IsRevealed()); 255 EXPECT_FALSE(delegate()->is_enabled()); 256 } 257 258 // GetRevealedLock() specific tests. 259 TEST_F(ImmersiveFullscreenControllerTest, RevealedLock) { 260 scoped_ptr<ImmersiveRevealedLock> lock1; 261 scoped_ptr<ImmersiveRevealedLock> lock2; 262 263 // Immersive fullscreen is not on by default. 264 EXPECT_FALSE(controller()->IsEnabled()); 265 266 // 1) Test acquiring and releasing a revealed state lock while immersive 267 // fullscreen is disabled. Acquiring or releasing the lock should have no 268 // effect till immersive fullscreen is enabled. 269 lock1.reset(controller()->GetRevealedLock( 270 ImmersiveFullscreenController::ANIMATE_REVEAL_NO)); 271 EXPECT_FALSE(controller()->IsEnabled()); 272 EXPECT_FALSE(controller()->IsRevealed()); 273 274 // Immersive fullscreen should start in the revealed state due to the lock. 275 SetEnabled(true); 276 EXPECT_TRUE(controller()->IsEnabled()); 277 EXPECT_TRUE(controller()->IsRevealed()); 278 279 SetEnabled(false); 280 EXPECT_FALSE(controller()->IsEnabled()); 281 EXPECT_FALSE(controller()->IsRevealed()); 282 283 lock1.reset(); 284 EXPECT_FALSE(controller()->IsEnabled()); 285 EXPECT_FALSE(controller()->IsRevealed()); 286 287 // Immersive fullscreen should start in the closed state because the lock is 288 // no longer held. 289 SetEnabled(true); 290 EXPECT_TRUE(controller()->IsEnabled()); 291 EXPECT_FALSE(controller()->IsRevealed()); 292 293 // 2) Test that acquiring a lock reveals the top-of-window views if they are 294 // hidden. 295 lock1.reset(controller()->GetRevealedLock( 296 ImmersiveFullscreenController::ANIMATE_REVEAL_NO)); 297 EXPECT_TRUE(controller()->IsRevealed()); 298 299 // 3) Test that the top-of-window views are only hidden when all of the locks 300 // are released. 301 lock2.reset(controller()->GetRevealedLock( 302 ImmersiveFullscreenController::ANIMATE_REVEAL_NO)); 303 lock1.reset(); 304 EXPECT_TRUE(controller()->IsRevealed()); 305 306 lock2.reset(); 307 EXPECT_FALSE(controller()->IsRevealed()); 308 } 309 310 // Test mouse event processing for top-of-screen reveal triggering. 311 TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) { 312 // Set up initial state. 313 UpdateDisplay("800x600,800x600"); 314 ash::DisplayLayout display_layout(ash::DisplayLayout::RIGHT, 0); 315 ash::Shell::GetInstance()->display_manager()->SetLayoutForCurrentDisplays( 316 display_layout); 317 318 // Set up initial state. 319 SetEnabled(true); 320 ASSERT_TRUE(controller()->IsEnabled()); 321 ASSERT_FALSE(controller()->IsRevealed()); 322 323 aura::test::EventGenerator& event_generator(GetEventGenerator()); 324 325 gfx::Rect top_container_bounds_in_screen = 326 top_container()->GetBoundsInScreen(); 327 // A position along the top edge of TopContainerView in screen coordinates. 328 gfx::Point top_edge_pos(top_container_bounds_in_screen.x() + 100, 329 top_container_bounds_in_screen.y()); 330 331 // Mouse wheel event does nothing. 332 ui::MouseEvent wheel( 333 ui::ET_MOUSEWHEEL, top_edge_pos, top_edge_pos, ui::EF_NONE); 334 event_generator.Dispatch(&wheel); 335 EXPECT_FALSE(top_edge_hover_timer_running()); 336 337 // Move to top edge of screen starts hover timer running. We cannot use 338 // MoveMouse() because MoveMouse() stops the timer if it started running. 339 event_generator.MoveMouseTo(top_edge_pos); 340 EXPECT_TRUE(top_edge_hover_timer_running()); 341 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top()); 342 343 // Moving |ImmersiveFullscreenControllerTest::kMouseRevealBoundsHeight| down 344 // from the top edge stops it. 345 event_generator.MoveMouseBy(0, 3); 346 EXPECT_FALSE(top_edge_hover_timer_running()); 347 348 // Moving back to the top starts the timer again. 349 event_generator.MoveMouseTo(top_edge_pos); 350 EXPECT_TRUE(top_edge_hover_timer_running()); 351 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top()); 352 353 // Slight move to the right keeps the timer running for the same hit point. 354 event_generator.MoveMouseBy(1, 0); 355 EXPECT_TRUE(top_edge_hover_timer_running()); 356 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top()); 357 358 // Moving back to the left also keeps the timer running. 359 event_generator.MoveMouseBy(-1, 0); 360 EXPECT_TRUE(top_edge_hover_timer_running()); 361 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top()); 362 363 // Large move right restarts the timer (so it is still running) and considers 364 // this a new hit at the top. 365 event_generator.MoveMouseTo(top_edge_pos.x() + 100, top_edge_pos.y()); 366 EXPECT_TRUE(top_edge_hover_timer_running()); 367 EXPECT_EQ(top_edge_pos.x() + 100, mouse_x_when_hit_top()); 368 369 // Moving off the top edge horizontally stops the timer. 370 event_generator.MoveMouseTo(top_container_bounds_in_screen.right() + 1, 371 top_container_bounds_in_screen.y()); 372 EXPECT_FALSE(top_edge_hover_timer_running()); 373 374 // Once revealed, a move just a little below the top container doesn't end a 375 // reveal. 376 AttemptReveal(MODALITY_MOUSE); 377 event_generator.MoveMouseTo(top_container_bounds_in_screen.x(), 378 top_container_bounds_in_screen.bottom() + 1); 379 EXPECT_TRUE(controller()->IsRevealed()); 380 381 // Once revealed, clicking just below the top container ends the reveal. 382 event_generator.ClickLeftButton(); 383 EXPECT_FALSE(controller()->IsRevealed()); 384 385 // Moving a lot below the top container ends a reveal. 386 AttemptReveal(MODALITY_MOUSE); 387 EXPECT_TRUE(controller()->IsRevealed()); 388 event_generator.MoveMouseTo(top_container_bounds_in_screen.x(), 389 top_container_bounds_in_screen.bottom() + 50); 390 EXPECT_FALSE(controller()->IsRevealed()); 391 392 // The mouse position cannot cause a reveal when the top container's widget 393 // has capture. 394 views::Widget* widget = top_container()->GetWidget(); 395 widget->SetCapture(top_container()); 396 AttemptReveal(MODALITY_MOUSE); 397 EXPECT_FALSE(controller()->IsRevealed()); 398 widget->ReleaseCapture(); 399 400 // The mouse position cannot end the reveal while the top container's widget 401 // has capture. 402 AttemptReveal(MODALITY_MOUSE); 403 EXPECT_TRUE(controller()->IsRevealed()); 404 widget->SetCapture(top_container()); 405 event_generator.MoveMouseTo(top_container_bounds_in_screen.x(), 406 top_container_bounds_in_screen.bottom() + 51); 407 EXPECT_TRUE(controller()->IsRevealed()); 408 409 // Releasing capture should end the reveal. 410 widget->ReleaseCapture(); 411 EXPECT_FALSE(controller()->IsRevealed()); 412 } 413 414 // Test mouse event processing for top-of-screen reveal triggering when the 415 // top container's widget is inactive. 416 TEST_F(ImmersiveFullscreenControllerTest, Inactive) { 417 // Set up initial state. 418 views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds( 419 NULL, 420 CurrentContext(), 421 gfx::Rect(0, 0, 200, 200)); 422 popup_widget->Show(); 423 ASSERT_FALSE(top_container()->GetWidget()->IsActive()); 424 425 SetEnabled(true); 426 ASSERT_TRUE(controller()->IsEnabled()); 427 ASSERT_FALSE(controller()->IsRevealed()); 428 429 gfx::Rect top_container_bounds_in_screen = 430 top_container()->GetBoundsInScreen(); 431 gfx::Rect popup_bounds_in_screen = popup_widget->GetWindowBoundsInScreen(); 432 ASSERT_EQ(top_container_bounds_in_screen.origin().ToString(), 433 popup_bounds_in_screen.origin().ToString()); 434 ASSERT_GT(top_container_bounds_in_screen.right(), 435 popup_bounds_in_screen.right()); 436 437 // The top-of-window views should stay hidden if the cursor is at the top edge 438 // but above an obscured portion of the top-of-window views. 439 MoveMouse(popup_bounds_in_screen.x(), 440 top_container_bounds_in_screen.y()); 441 EXPECT_FALSE(controller()->IsRevealed()); 442 443 // The top-of-window views should reveal if the cursor is at the top edge and 444 // above an unobscured portion of the top-of-window views. 445 MoveMouse(top_container_bounds_in_screen.right() - 1, 446 top_container_bounds_in_screen.y()); 447 EXPECT_TRUE(controller()->IsRevealed()); 448 449 // The top-of-window views should stay revealed if the cursor is moved off 450 // of the top edge. 451 MoveMouse(top_container_bounds_in_screen.right() - 1, 452 top_container_bounds_in_screen.bottom() - 1); 453 EXPECT_TRUE(controller()->IsRevealed()); 454 455 // Moving way off of the top-of-window views should end the immersive reveal. 456 MoveMouse(top_container_bounds_in_screen.right() - 1, 457 top_container_bounds_in_screen.bottom() + 50); 458 EXPECT_FALSE(controller()->IsRevealed()); 459 460 // Moving way off of the top-of-window views in a region where the 461 // top-of-window views are obscured should also end the immersive reveal. 462 // Ideally, the immersive reveal would end immediately when the cursor moves 463 // to an obscured portion of the top-of-window views. 464 MoveMouse(top_container_bounds_in_screen.right() - 1, 465 top_container_bounds_in_screen.y()); 466 EXPECT_TRUE(controller()->IsRevealed()); 467 MoveMouse(top_container_bounds_in_screen.x(), 468 top_container_bounds_in_screen.bottom() + 50); 469 EXPECT_FALSE(controller()->IsRevealed()); 470 } 471 472 // Test mouse event processing for top-of-screen reveal triggering when the user 473 // has a vertical display layout (primary display above/below secondary display) 474 // and the immersive fullscreen window is on the bottom display. 475 TEST_F(ImmersiveFullscreenControllerTest, MouseEventsVerticalDisplayLayout) { 476 if (!SupportsMultipleDisplays()) 477 return; 478 479 // Set up initial state. 480 UpdateDisplay("800x600,800x600"); 481 ash::DisplayLayout display_layout(ash::DisplayLayout::TOP, 0); 482 ash::Shell::GetInstance()->display_manager()->SetLayoutForCurrentDisplays( 483 display_layout); 484 485 SetEnabled(true); 486 ASSERT_TRUE(controller()->IsEnabled()); 487 ASSERT_FALSE(controller()->IsRevealed()); 488 489 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows(); 490 ASSERT_EQ(root_windows[0], 491 top_container()->GetWidget()->GetNativeWindow()->GetRootWindow()); 492 493 gfx::Rect primary_root_window_bounds_in_screen = 494 root_windows[0]->GetBoundsInScreen(); 495 // Do not set |x| to the root window's x position because the display's 496 // corners have special behavior. 497 int x = primary_root_window_bounds_in_screen.x() + 10; 498 // The y position of the top edge of the primary display. 499 int y_top_edge = primary_root_window_bounds_in_screen.y(); 500 501 aura::test::EventGenerator& event_generator(GetEventGenerator()); 502 503 // Moving right below the top edge starts the hover timer running. We 504 // cannot use MoveMouse() because MoveMouse() stops the timer if it started 505 // running. 506 event_generator.MoveMouseTo(x, y_top_edge + 1); 507 EXPECT_TRUE(top_edge_hover_timer_running()); 508 EXPECT_EQ(y_top_edge + 1, 509 aura::Env::GetInstance()->last_mouse_location().y()); 510 511 // The timer should continue running if the user moves the mouse to the top 512 // edge even though the mouse is warped to the secondary display. 513 event_generator.MoveMouseTo(x, y_top_edge); 514 EXPECT_TRUE(top_edge_hover_timer_running()); 515 EXPECT_NE(y_top_edge, 516 aura::Env::GetInstance()->last_mouse_location().y()); 517 518 // The timer should continue running if the user overshoots the top edge 519 // a bit. 520 event_generator.MoveMouseTo(x, y_top_edge - 2); 521 EXPECT_TRUE(top_edge_hover_timer_running()); 522 523 // The timer should stop running if the user overshoots the top edge by 524 // a lot. 525 event_generator.MoveMouseTo(x, y_top_edge - 20); 526 EXPECT_FALSE(top_edge_hover_timer_running()); 527 528 // The timer should not start if the user moves the mouse to the bottom of the 529 // secondary display without crossing the top edge first. 530 event_generator.MoveMouseTo(x, y_top_edge - 2); 531 532 // Reveal the top-of-window views by overshooting the top edge slightly. 533 event_generator.MoveMouseTo(x, y_top_edge + 1); 534 // MoveMouse() runs the timer task. 535 MoveMouse(x, y_top_edge - 2); 536 EXPECT_TRUE(controller()->IsRevealed()); 537 538 // The top-of-window views should stay revealed if the user moves the mouse 539 // around in the bottom region of the secondary display. 540 event_generator.MoveMouseTo(x + 10, y_top_edge - 3); 541 EXPECT_TRUE(controller()->IsRevealed()); 542 543 // The top-of-window views should hide if the user moves the mouse away from 544 // the bottom region of the secondary display. 545 event_generator.MoveMouseTo(x, y_top_edge - 20); 546 EXPECT_FALSE(controller()->IsRevealed()); 547 548 // Test that it is possible to reveal the top-of-window views by overshooting 549 // the top edge slightly when the top container's widget is not active. 550 views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds( 551 NULL, 552 CurrentContext(), 553 gfx::Rect(0, 200, 100, 100)); 554 popup_widget->Show(); 555 ASSERT_FALSE(top_container()->GetWidget()->IsActive()); 556 ASSERT_FALSE(top_container()->GetBoundsInScreen().Intersects( 557 popup_widget->GetWindowBoundsInScreen())); 558 event_generator.MoveMouseTo(x, y_top_edge + 1); 559 MoveMouse(x, y_top_edge - 2); 560 EXPECT_TRUE(controller()->IsRevealed()); 561 } 562 563 // Test behavior when the mouse becomes hovered without moving. 564 TEST_F(ImmersiveFullscreenControllerTest, MouseHoveredWithoutMoving) { 565 SetEnabled(true); 566 scoped_ptr<ImmersiveRevealedLock> lock; 567 568 // 1) Test that if the mouse becomes hovered without the mouse moving due to a 569 // lock causing the top-of-window views to be revealed (and the mouse 570 // happening to be near the top of the screen), the top-of-window views do not 571 // hide till the mouse moves off of the top-of-window views. 572 SetHovered(true); 573 EXPECT_FALSE(controller()->IsRevealed()); 574 lock.reset(controller()->GetRevealedLock( 575 ImmersiveFullscreenController::ANIMATE_REVEAL_NO)); 576 EXPECT_TRUE(controller()->IsRevealed()); 577 lock.reset(); 578 EXPECT_TRUE(controller()->IsRevealed()); 579 SetHovered(false); 580 EXPECT_FALSE(controller()->IsRevealed()); 581 582 // 2) Test that if the mouse becomes hovered without moving because of a 583 // reveal in ImmersiveFullscreenController::SetEnabled(true) and there are no 584 // locks keeping the top-of-window views revealed, that mouse hover does not 585 // prevent the top-of-window views from closing. 586 SetEnabled(false); 587 SetHovered(true); 588 EXPECT_FALSE(controller()->IsRevealed()); 589 SetEnabled(true); 590 EXPECT_FALSE(controller()->IsRevealed()); 591 592 // 3) Test that if the mouse becomes hovered without moving because of a 593 // reveal in ImmersiveFullscreenController::SetEnabled(true) and there is a 594 // lock keeping the top-of-window views revealed, that the top-of-window views 595 // do not hide till the mouse moves off of the top-of-window views. 596 SetEnabled(false); 597 SetHovered(true); 598 lock.reset(controller()->GetRevealedLock( 599 ImmersiveFullscreenController::ANIMATE_REVEAL_NO)); 600 EXPECT_FALSE(controller()->IsRevealed()); 601 SetEnabled(true); 602 EXPECT_TRUE(controller()->IsRevealed()); 603 lock.reset(); 604 EXPECT_TRUE(controller()->IsRevealed()); 605 SetHovered(false); 606 EXPECT_FALSE(controller()->IsRevealed()); 607 } 608 609 // Test revealing the top-of-window views using one modality and ending 610 // the reveal via another. For instance, initiating the reveal via a SWIPE_OPEN 611 // edge gesture, switching to using the mouse and ending the reveal by moving 612 // the mouse off of the top-of-window views. 613 TEST_F(ImmersiveFullscreenControllerTest, DifferentModalityEnterExit) { 614 SetEnabled(true); 615 EXPECT_TRUE(controller()->IsEnabled()); 616 EXPECT_FALSE(controller()->IsRevealed()); 617 618 // Initiate reveal via gesture, end reveal via mouse. 619 AttemptReveal(MODALITY_GESTURE); 620 EXPECT_TRUE(controller()->IsRevealed()); 621 MoveMouse(1, 1); 622 EXPECT_TRUE(controller()->IsRevealed()); 623 AttemptUnreveal(MODALITY_MOUSE); 624 EXPECT_FALSE(controller()->IsRevealed()); 625 626 // Initiate reveal via gesture, end reveal via touch. 627 AttemptReveal(MODALITY_GESTURE); 628 EXPECT_TRUE(controller()->IsRevealed()); 629 AttemptUnreveal(MODALITY_TOUCH); 630 EXPECT_FALSE(controller()->IsRevealed()); 631 632 // Initiate reveal via mouse, end reveal via gesture. 633 AttemptReveal(MODALITY_MOUSE); 634 EXPECT_TRUE(controller()->IsRevealed()); 635 AttemptUnreveal(MODALITY_GESTURE); 636 EXPECT_FALSE(controller()->IsRevealed()); 637 638 // Initiate reveal via mouse, end reveal via touch. 639 AttemptReveal(MODALITY_MOUSE); 640 EXPECT_TRUE(controller()->IsRevealed()); 641 AttemptUnreveal(MODALITY_TOUCH); 642 EXPECT_FALSE(controller()->IsRevealed()); 643 } 644 645 // Test when the SWIPE_CLOSE edge gesture closes the top-of-window views. 646 TEST_F(ImmersiveFullscreenControllerTest, EndRevealViaGesture) { 647 SetEnabled(true); 648 EXPECT_TRUE(controller()->IsEnabled()); 649 EXPECT_FALSE(controller()->IsRevealed()); 650 651 // A gesture should be able to close the top-of-window views when 652 // top-of-window views have focus. 653 AttemptReveal(MODALITY_MOUSE); 654 top_container()->RequestFocus(); 655 EXPECT_TRUE(controller()->IsRevealed()); 656 AttemptUnreveal(MODALITY_GESTURE); 657 EXPECT_FALSE(controller()->IsRevealed()); 658 659 // The top-of-window views should no longer have focus. Clearing focus is 660 // important because it closes focus-related popup windows like the touch 661 // selection handles. 662 EXPECT_FALSE(top_container()->HasFocus()); 663 664 // If some other code is holding onto a lock, a gesture should not be able to 665 // end the reveal. 666 AttemptReveal(MODALITY_MOUSE); 667 scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock( 668 ImmersiveFullscreenController::ANIMATE_REVEAL_NO)); 669 EXPECT_TRUE(controller()->IsRevealed()); 670 AttemptUnreveal(MODALITY_GESTURE); 671 EXPECT_TRUE(controller()->IsRevealed()); 672 lock.reset(); 673 EXPECT_FALSE(controller()->IsRevealed()); 674 } 675 676 // Do not test under windows because focus testing is not reliable on 677 // Windows. (crbug.com/79493) 678 #if !defined(OS_WIN) 679 680 // Test how focus and activation affects whether the top-of-window views are 681 // revealed. 682 TEST_F(ImmersiveFullscreenControllerTest, Focus) { 683 // Add views to the view hierarchy which we will focus and unfocus during the 684 // test. 685 views::View* child_view = new views::View(); 686 child_view->SetBounds(0, 0, 10, 10); 687 child_view->SetFocusable(true); 688 top_container()->AddChildView(child_view); 689 views::View* unrelated_view = new views::View(); 690 unrelated_view->SetBounds(0, 100, 10, 10); 691 unrelated_view->SetFocusable(true); 692 top_container()->parent()->AddChildView(unrelated_view); 693 views::FocusManager* focus_manager = 694 top_container()->GetWidget()->GetFocusManager(); 695 696 SetEnabled(true); 697 698 // 1) Test that the top-of-window views stay revealed as long as either a 699 // |child_view| has focus or the mouse is hovered above the top-of-window 700 // views. 701 AttemptReveal(MODALITY_MOUSE); 702 child_view->RequestFocus(); 703 focus_manager->ClearFocus(); 704 EXPECT_TRUE(controller()->IsRevealed()); 705 child_view->RequestFocus(); 706 SetHovered(false); 707 EXPECT_TRUE(controller()->IsRevealed()); 708 focus_manager->ClearFocus(); 709 EXPECT_FALSE(controller()->IsRevealed()); 710 711 // 2) Test that focusing |unrelated_view| hides the top-of-window views. 712 // Note: In this test we can cheat and trigger a reveal via focus because 713 // the top container does not hide when the top-of-window views are not 714 // revealed. 715 child_view->RequestFocus(); 716 EXPECT_TRUE(controller()->IsRevealed()); 717 unrelated_view->RequestFocus(); 718 EXPECT_FALSE(controller()->IsRevealed()); 719 720 // 3) Test that a loss of focus of |child_view| to |unrelated_view| 721 // while immersive mode is disabled is properly registered. 722 child_view->RequestFocus(); 723 EXPECT_TRUE(controller()->IsRevealed()); 724 SetEnabled(false); 725 EXPECT_FALSE(controller()->IsRevealed()); 726 unrelated_view->RequestFocus(); 727 SetEnabled(true); 728 EXPECT_FALSE(controller()->IsRevealed()); 729 730 // Repeat test but with a revealed lock acquired when immersive mode is 731 // disabled because the code path is different. 732 child_view->RequestFocus(); 733 EXPECT_TRUE(controller()->IsRevealed()); 734 SetEnabled(false); 735 scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock( 736 ImmersiveFullscreenController::ANIMATE_REVEAL_NO)); 737 EXPECT_FALSE(controller()->IsRevealed()); 738 unrelated_view->RequestFocus(); 739 SetEnabled(true); 740 EXPECT_TRUE(controller()->IsRevealed()); 741 lock.reset(); 742 EXPECT_FALSE(controller()->IsRevealed()); 743 } 744 745 // Test how transient windows affect whether the top-of-window views are 746 // revealed. 747 TEST_F(ImmersiveFullscreenControllerTest, Transient) { 748 views::Widget* top_container_widget = top_container()->GetWidget(); 749 750 SetEnabled(true); 751 ASSERT_FALSE(controller()->IsRevealed()); 752 753 // 1) Test that a transient window which is not a bubble does not trigger a 754 // reveal but does keep the top-of-window views revealed if they are already 755 // revealed. 756 views::Widget::InitParams transient_params; 757 transient_params.ownership = 758 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 759 transient_params.parent = top_container_widget->GetNativeView(); 760 transient_params.bounds = gfx::Rect(0, 100, 100, 100); 761 scoped_ptr<views::Widget> transient_widget(new views::Widget()); 762 transient_widget->Init(transient_params); 763 764 EXPECT_FALSE(controller()->IsRevealed()); 765 AttemptReveal(MODALITY_MOUSE); 766 EXPECT_TRUE(controller()->IsRevealed()); 767 transient_widget->Show(); 768 SetHovered(false); 769 EXPECT_TRUE(controller()->IsRevealed()); 770 transient_widget.reset(); 771 EXPECT_FALSE(controller()->IsRevealed()); 772 773 // 2) Test that activating a non-transient window does not keep the 774 // top-of-window views revealed. 775 views::Widget::InitParams non_transient_params; 776 non_transient_params.ownership = 777 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 778 non_transient_params.context = top_container_widget->GetNativeView(); 779 non_transient_params.bounds = gfx::Rect(0, 100, 100, 100); 780 scoped_ptr<views::Widget> non_transient_widget(new views::Widget()); 781 non_transient_widget->Init(non_transient_params); 782 783 EXPECT_FALSE(controller()->IsRevealed()); 784 AttemptReveal(MODALITY_MOUSE); 785 EXPECT_TRUE(controller()->IsRevealed()); 786 non_transient_widget->Show(); 787 SetHovered(false); 788 EXPECT_FALSE(controller()->IsRevealed()); 789 } 790 791 // Test how bubbles affect whether the top-of-window views are revealed. 792 TEST_F(ImmersiveFullscreenControllerTest, Bubbles) { 793 scoped_ptr<ImmersiveRevealedLock> revealed_lock; 794 views::Widget* top_container_widget = top_container()->GetWidget(); 795 796 // Add views to the view hierarchy to which we will anchor bubbles. 797 views::View* child_view = new views::View(); 798 child_view->SetBounds(0, 0, 10, 10); 799 top_container()->AddChildView(child_view); 800 views::View* unrelated_view = new views::View(); 801 unrelated_view->SetBounds(0, 100, 10, 10); 802 top_container()->parent()->AddChildView(unrelated_view); 803 804 SetEnabled(true); 805 ASSERT_FALSE(controller()->IsRevealed()); 806 807 // 1) Test that a bubble anchored to a child of the top container triggers 808 // a reveal and keeps the top-of-window views revealed for the duration of 809 // its visibility. 810 views::Widget* bubble_widget1(views::BubbleDelegateView::CreateBubble( 811 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE))); 812 bubble_widget1->Show(); 813 EXPECT_TRUE(controller()->IsRevealed()); 814 815 // Activating |top_container_widget| will close |bubble_widget1|. 816 top_container_widget->Activate(); 817 AttemptReveal(MODALITY_MOUSE); 818 revealed_lock.reset(controller()->GetRevealedLock( 819 ImmersiveFullscreenController::ANIMATE_REVEAL_NO)); 820 EXPECT_TRUE(controller()->IsRevealed()); 821 822 views::Widget* bubble_widget2 = views::BubbleDelegateView::CreateBubble( 823 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE)); 824 bubble_widget2->Show(); 825 EXPECT_TRUE(controller()->IsRevealed()); 826 revealed_lock.reset(); 827 SetHovered(false); 828 EXPECT_TRUE(controller()->IsRevealed()); 829 bubble_widget2->Close(); 830 EXPECT_FALSE(controller()->IsRevealed()); 831 832 // 2) Test that transitioning from keeping the top-of-window views revealed 833 // because of a bubble to keeping the top-of-window views revealed because of 834 // mouse hover by activating |top_container_widget| works. 835 views::Widget* bubble_widget3 = views::BubbleDelegateView::CreateBubble( 836 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE)); 837 bubble_widget3->Show(); 838 SetHovered(true); 839 EXPECT_TRUE(controller()->IsRevealed()); 840 top_container_widget->Activate(); 841 EXPECT_TRUE(controller()->IsRevealed()); 842 843 // 3) Test that the top-of-window views stay revealed as long as at least one 844 // bubble anchored to a child of the top container is visible. 845 SetHovered(false); 846 EXPECT_FALSE(controller()->IsRevealed()); 847 848 views::BubbleDelegateView* bubble_delegate4(new views::BubbleDelegateView( 849 child_view, views::BubbleBorder::NONE)); 850 bubble_delegate4->set_use_focusless(true); 851 views::Widget* bubble_widget4(views::BubbleDelegateView::CreateBubble( 852 bubble_delegate4)); 853 bubble_widget4->Show(); 854 855 views::BubbleDelegateView* bubble_delegate5(new views::BubbleDelegateView( 856 child_view, views::BubbleBorder::NONE)); 857 bubble_delegate5->set_use_focusless(true); 858 views::Widget* bubble_widget5(views::BubbleDelegateView::CreateBubble( 859 bubble_delegate5)); 860 bubble_widget5->Show(); 861 862 EXPECT_TRUE(controller()->IsRevealed()); 863 bubble_widget4->Hide(); 864 EXPECT_TRUE(controller()->IsRevealed()); 865 bubble_widget5->Hide(); 866 EXPECT_FALSE(controller()->IsRevealed()); 867 bubble_widget5->Show(); 868 EXPECT_TRUE(controller()->IsRevealed()); 869 870 // 4) Test that visibility changes which occur while immersive fullscreen is 871 // disabled are handled upon reenabling immersive fullscreen. 872 SetEnabled(false); 873 bubble_widget5->Hide(); 874 SetEnabled(true); 875 EXPECT_FALSE(controller()->IsRevealed()); 876 877 // We do not need |bubble_widget4| or |bubble_widget5| anymore, close them. 878 bubble_widget4->Close(); 879 bubble_widget5->Close(); 880 881 // 5) Test that a bubble added while immersive fullscreen is disabled is 882 // handled upon reenabling immersive fullscreen. 883 SetEnabled(false); 884 885 views::Widget* bubble_widget6 = views::BubbleDelegateView::CreateBubble( 886 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE)); 887 bubble_widget6->Show(); 888 889 SetEnabled(true); 890 EXPECT_TRUE(controller()->IsRevealed()); 891 892 bubble_widget6->Close(); 893 894 // 6) Test that a bubble which is not anchored to a child of the 895 // TopContainerView does not trigger a reveal or keep the 896 // top-of-window views revealed if they are already revealed. 897 views::Widget* bubble_widget7 = views::BubbleDelegateView::CreateBubble( 898 new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE)); 899 bubble_widget7->Show(); 900 EXPECT_FALSE(controller()->IsRevealed()); 901 902 // Activating |top_container_widget| will close |bubble_widget6|. 903 top_container_widget->Activate(); 904 AttemptReveal(MODALITY_MOUSE); 905 EXPECT_TRUE(controller()->IsRevealed()); 906 907 views::Widget* bubble_widget8 = views::BubbleDelegateView::CreateBubble( 908 new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE)); 909 bubble_widget8->Show(); 910 SetHovered(false); 911 EXPECT_FALSE(controller()->IsRevealed()); 912 bubble_widget8->Close(); 913 } 914 915 #endif // defined(OS_WIN) 916 917 // Test that the shelf is set to auto hide as long as the window is in 918 // immersive fullscreen and that the shelf's state before entering immersive 919 // fullscreen is restored upon exiting immersive fullscreen. 920 TEST_F(ImmersiveFullscreenControllerTest, Shelf) { 921 ash::internal::ShelfLayoutManager* shelf = 922 ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager(); 923 924 // Shelf is visible by default. 925 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 926 ASSERT_FALSE(controller()->IsEnabled()); 927 ASSERT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state()); 928 929 // Entering immersive fullscreen sets the shelf to auto hide. 930 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 931 SetEnabled(true); 932 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); 933 934 // Disabling immersive fullscreen puts it back. 935 SetEnabled(false); 936 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 937 ASSERT_FALSE(controller()->IsEnabled()); 938 EXPECT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state()); 939 940 // The user could toggle the shelf auto-hide behavior. 941 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); 942 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); 943 944 // Entering immersive fullscreen keeps auto-hide. 945 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 946 SetEnabled(true); 947 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); 948 949 // Disabling immersive fullscreen maintains the user's auto-hide selection. 950 SetEnabled(false); 951 window()->SetProperty(aura::client::kShowStateKey, 952 ui::SHOW_STATE_NORMAL); 953 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); 954 } 955 956 } // namespase ash 957 958 #endif // defined(OS_CHROMEOS) 959