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