immersive_mode_controller_ash_unittest.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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 "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h" 6 7#include "ash/display/display_controller.h" 8#include "ash/shell.h" 9#include "ash/test/ash_test_base.h" 10#include "chrome/browser/ui/immersive_fullscreen_configuration.h" 11#include "ui/aura/client/cursor_client.h" 12#include "ui/aura/env.h" 13#include "ui/aura/root_window.h" 14#include "ui/aura/test/event_generator.h" 15#include "ui/aura/window.h" 16#include "ui/gfx/animation/slide_animation.h" 17#include "ui/views/bubble/bubble_delegate.h" 18 19// For now, immersive fullscreen is Chrome OS only. 20#if defined(OS_CHROMEOS) 21 22///////////////////////////////////////////////////////////////////////////// 23 24class MockImmersiveModeControllerDelegate 25 : public ImmersiveModeController::Delegate { 26 public: 27 MockImmersiveModeControllerDelegate() : immersive_style_(false) {} 28 virtual ~MockImmersiveModeControllerDelegate() {} 29 30 bool immersive_style() const { return immersive_style_; } 31 32 // ImmersiveModeController::Delegate overrides: 33 virtual BookmarkBarView* GetBookmarkBar() OVERRIDE { return NULL; } 34 virtual FullscreenController* GetFullscreenController() OVERRIDE { 35 return NULL; 36 } 37 virtual void FullscreenStateChanged() OVERRIDE {} 38 virtual void SetImmersiveStyle(bool immersive) OVERRIDE { 39 immersive_style_ = immersive; 40 } 41 virtual content::WebContents* GetWebContents() OVERRIDE { 42 return NULL; 43 } 44 45 private: 46 bool immersive_style_; 47 48 DISALLOW_COPY_AND_ASSIGN(MockImmersiveModeControllerDelegate); 49}; 50 51///////////////////////////////////////////////////////////////////////////// 52 53class ImmersiveModeControllerAshTest : public ash::test::AshTestBase { 54 public: 55 enum Modality { 56 MODALITY_MOUSE, 57 MODALITY_TOUCH, 58 MODALITY_GESTURE 59 }; 60 61 ImmersiveModeControllerAshTest() : widget_(NULL), top_container_(NULL) {} 62 virtual ~ImmersiveModeControllerAshTest() {} 63 64 ImmersiveModeControllerAsh* controller() { return controller_.get(); } 65 views::View* top_container() { return top_container_; } 66 MockImmersiveModeControllerDelegate* delegate() { return delegate_.get(); } 67 68 // Access to private data from the controller. 69 bool top_edge_hover_timer_running() const { 70 return controller_->top_edge_hover_timer_.IsRunning(); 71 } 72 int mouse_x_when_hit_top() const { 73 return controller_->mouse_x_when_hit_top_in_screen_; 74 } 75 76 // ash::test::AshTestBase overrides: 77 virtual void SetUp() OVERRIDE { 78 ash::test::AshTestBase::SetUp(); 79 80 ImmersiveFullscreenConfiguration::EnableImmersiveFullscreenForTest(); 81 ASSERT_TRUE(ImmersiveFullscreenConfiguration::UseImmersiveFullscreen()); 82 83 controller_.reset(new ImmersiveModeControllerAsh); 84 delegate_.reset(new MockImmersiveModeControllerDelegate); 85 86 widget_ = new views::Widget(); 87 views::Widget::InitParams params; 88 params.context = CurrentContext(); 89 params.bounds = gfx::Rect(0, 0, 500, 500); 90 widget_->Init(params); 91 widget_->Show(); 92 93 top_container_ = new views::View(); 94 top_container_->SetBounds(0, 0, 500, 100); 95 top_container_->set_focusable(true); 96 97 widget_->GetContentsView()->AddChildView(top_container_); 98 99 controller_->Init(delegate_.get(), widget_, top_container_); 100 SetAnimationsDisabled(true); 101 } 102 103 // Enable or disable the immersive mode controller's animations. When the 104 // immersive mode controller's animations are disabled, some behavior is 105 // slightly different. In particular, the behavior is different when there 106 // is a transfer in which lock keeps the top-of-window views revealed (eg 107 // bubble keeps top-of-window views revealed -> mouse keeps top-of-window 108 // views revealed). It is necessary to temparily enable the immersive 109 // controller's animations to get the correct behavior in tests. 110 void SetAnimationsDisabled(bool disabled) { 111 controller_->animations_disabled_for_test_ = disabled; 112 // Force any in progress animations to finish. 113 if (disabled) 114 controller_->animation_->End(); 115 } 116 117 // Attempt to reveal the top-of-window views via |modality|. 118 // The top-of-window views can only be revealed via mouse hover or a gesture. 119 void AttemptReveal(Modality modality) { 120 ASSERT_NE(modality, MODALITY_TOUCH); 121 AttemptRevealStateChange(true, modality); 122 } 123 124 // Attempt to unreveal the top-of-window views via |modality|. The 125 // top-of-window views can be unrevealed via any modality. 126 void AttemptUnreveal(Modality modality) { 127 AttemptRevealStateChange(false, modality); 128 } 129 130 // Sets whether the mouse is hovered above |top_container_|. 131 // SetHovered(true) moves the mouse over the |top_container_| but does not 132 // move it to the top of the screen so will not initiate a reveal. 133 void SetHovered(bool is_mouse_hovered) { 134 MoveMouse(0, is_mouse_hovered ? 1 : top_container_->height() + 100); 135 } 136 137 // Move the mouse to the given coordinates. The coordinates should be in 138 // |top_container_| coordinates. 139 void MoveMouse(int x, int y) { 140 gfx::Point screen_position(x, y); 141 views::View::ConvertPointToScreen(top_container_, &screen_position); 142 GetEventGenerator().MoveMouseTo(screen_position.x(), screen_position.y()); 143 144 // If the top edge timer started running as a result of the mouse move, run 145 // the task which occurs after the timer delay. This reveals the 146 // top-of-window views synchronously if the mouse is hovered at the top of 147 // the screen. 148 if (controller()->top_edge_hover_timer_.IsRunning()) { 149 controller()->top_edge_hover_timer_.user_task().Run(); 150 controller()->top_edge_hover_timer_.Stop(); 151 } 152 } 153 154 private: 155 // Attempt to change the revealed state to |revealed| via |modality|. 156 void AttemptRevealStateChange(bool revealed, Modality modality) { 157 // Compute the event position in |top_container_| coordinates. 158 gfx::Point event_position(0, revealed ? 0 : top_container_->height() + 100); 159 switch (modality) { 160 case MODALITY_MOUSE: { 161 MoveMouse(event_position.x(), event_position.y()); 162 break; 163 } 164 case MODALITY_TOUCH: { 165 gfx::Point screen_position = event_position; 166 views::View::ConvertPointToScreen(top_container_, &screen_position); 167 168 aura::test::EventGenerator& event_generator(GetEventGenerator()); 169 event_generator.MoveTouch(event_position); 170 event_generator.PressTouch(); 171 event_generator.ReleaseTouch(); 172 break; 173 } 174 case MODALITY_GESTURE: { 175 aura::client::GetCursorClient(CurrentContext())->DisableMouseEvents(); 176 ImmersiveModeControllerAsh::SwipeType swipe_type = revealed ? 177 ImmersiveModeControllerAsh::SWIPE_OPEN : 178 ImmersiveModeControllerAsh::SWIPE_CLOSE; 179 controller_->UpdateRevealedLocksForSwipe(swipe_type); 180 break; 181 } 182 } 183 } 184 185 scoped_ptr<ImmersiveModeControllerAsh> controller_; 186 scoped_ptr<MockImmersiveModeControllerDelegate> delegate_; 187 views::Widget* widget_; // Owned by the native widget. 188 views::View* top_container_; // Owned by |root_view_|. 189 scoped_ptr<aura::test::EventGenerator> event_generator_; 190 191 DISALLOW_COPY_AND_ASSIGN(ImmersiveModeControllerAshTest); 192}; 193 194// Test of initial state and basic functionality. 195TEST_F(ImmersiveModeControllerAshTest, ImmersiveModeControllerAsh) { 196 // Initial state. 197 EXPECT_FALSE(controller()->IsEnabled()); 198 EXPECT_FALSE(controller()->ShouldHideTopViews()); 199 EXPECT_FALSE(controller()->IsRevealed()); 200 EXPECT_FALSE(delegate()->immersive_style()); 201 202 // Enabling hides the top views. 203 controller()->SetEnabled(true); 204 EXPECT_TRUE(controller()->IsEnabled()); 205 EXPECT_FALSE(controller()->IsRevealed()); 206 EXPECT_TRUE(controller()->ShouldHideTopViews()); 207 EXPECT_FALSE(controller()->ShouldHideTabIndicators()); 208 EXPECT_TRUE(delegate()->immersive_style()); 209 210 // Revealing shows the top views. 211 AttemptReveal(MODALITY_MOUSE); 212 EXPECT_TRUE(controller()->IsRevealed()); 213 EXPECT_FALSE(controller()->ShouldHideTopViews()); 214 // Tabs are painting in the normal style during a reveal. 215 EXPECT_FALSE(delegate()->immersive_style()); 216} 217 218// Test mouse event processing for top-of-screen reveal triggering. 219TEST_F(ImmersiveModeControllerAshTest, OnMouseEvent) { 220 // Set up initial state. 221 controller()->SetEnabled(true); 222 ASSERT_TRUE(controller()->IsEnabled()); 223 ASSERT_FALSE(controller()->IsRevealed()); 224 225 aura::test::EventGenerator& event_generator(GetEventGenerator()); 226 227 gfx::Rect top_container_bounds_in_screen = 228 top_container()->GetBoundsInScreen(); 229 // A position along the top edge of TopContainerView in screen coordinates. 230 gfx::Point top_edge_pos(top_container_bounds_in_screen.x() + 100, 231 top_container_bounds_in_screen.y()); 232 233 // Mouse wheel event does nothing. 234 ui::MouseEvent wheel( 235 ui::ET_MOUSEWHEEL, top_edge_pos, top_edge_pos, ui::EF_NONE); 236 event_generator.Dispatch(&wheel); 237 EXPECT_FALSE(top_edge_hover_timer_running()); 238 239 // Move to top edge of screen starts hover timer running. We cannot use 240 // MoveMouse() because MoveMouse() stops the timer if it started running. 241 event_generator.MoveMouseTo(top_edge_pos); 242 EXPECT_TRUE(top_edge_hover_timer_running()); 243 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top()); 244 245 // Moving |ImmersiveModeControllerAsh::kMouseRevealBoundsHeight| down from 246 // the top edge stops it. 247 event_generator.MoveMouseBy(0, 3); 248 EXPECT_FALSE(top_edge_hover_timer_running()); 249 250 // Moving back to the top starts the timer again. 251 event_generator.MoveMouseTo(top_edge_pos); 252 EXPECT_TRUE(top_edge_hover_timer_running()); 253 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top()); 254 255 // Slight move to the right keeps the timer running for the same hit point. 256 event_generator.MoveMouseBy(1, 0); 257 EXPECT_TRUE(top_edge_hover_timer_running()); 258 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top()); 259 260 // Moving back to the left also keeps the timer running. 261 event_generator.MoveMouseBy(-1, 0); 262 EXPECT_TRUE(top_edge_hover_timer_running()); 263 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top()); 264 265 // Large move right restarts the timer (so it is still running) and considers 266 // this a new hit at the top. 267 event_generator.MoveMouseTo(top_edge_pos.x() + 100, top_edge_pos.y()); 268 EXPECT_TRUE(top_edge_hover_timer_running()); 269 EXPECT_EQ(top_edge_pos.x() + 100, mouse_x_when_hit_top()); 270 271 // Moving off the top edge horizontally stops the timer. 272 EXPECT_GT(CurrentContext()->bounds().width(), top_container()->width()); 273 event_generator.MoveMouseTo(top_container_bounds_in_screen.right(), 274 top_container_bounds_in_screen.y()); 275 EXPECT_FALSE(top_edge_hover_timer_running()); 276 277 // Once revealed, a move just a little below the top container doesn't end a 278 // reveal. 279 AttemptReveal(MODALITY_MOUSE); 280 event_generator.MoveMouseTo(top_container_bounds_in_screen.x(), 281 top_container_bounds_in_screen.bottom() + 1); 282 EXPECT_TRUE(controller()->IsRevealed()); 283 284 // Once revealed, clicking just below the top container ends the reveal. 285 event_generator.ClickLeftButton(); 286 EXPECT_FALSE(controller()->IsRevealed()); 287 288 // Moving a lot below the top container ends a reveal. 289 AttemptReveal(MODALITY_MOUSE); 290 EXPECT_TRUE(controller()->IsRevealed()); 291 event_generator.MoveMouseTo(top_container_bounds_in_screen.x(), 292 top_container_bounds_in_screen.bottom() + 50); 293 EXPECT_FALSE(controller()->IsRevealed()); 294 295 // The mouse position cannot cause a reveal when TopContainerView's widget 296 // has capture. 297 views::Widget* widget = top_container()->GetWidget(); 298 widget->SetCapture(top_container()); 299 AttemptReveal(MODALITY_MOUSE); 300 EXPECT_FALSE(controller()->IsRevealed()); 301 widget->ReleaseCapture(); 302 303 // The mouse position cannot end the reveal while TopContainerView's widget 304 // has capture. 305 AttemptReveal(MODALITY_MOUSE); 306 EXPECT_TRUE(controller()->IsRevealed()); 307 widget->SetCapture(top_container()); 308 event_generator.MoveMouseTo(top_container_bounds_in_screen.x(), 309 top_container_bounds_in_screen.bottom() + 51); 310 EXPECT_TRUE(controller()->IsRevealed()); 311 312 // Releasing capture should end the reveal. 313 widget->ReleaseCapture(); 314 EXPECT_FALSE(controller()->IsRevealed()); 315} 316 317// Test mouse event processing for top-of-screen reveal triggering when the user 318// has a vertical display layout (primary display above/below secondary display) 319// and the immersive fullscreen window is on the bottom display. 320TEST_F(ImmersiveModeControllerAshTest, MouseEventsVerticalDisplayLayout) { 321 if (!SupportsMultipleDisplays()) 322 return; 323 324 // Set up initial state. 325 UpdateDisplay("800x600,800x600"); 326 ash::DisplayLayout display_layout(ash::DisplayLayout::TOP, 0); 327 ash::Shell::GetInstance()->display_controller()->SetLayoutForCurrentDisplays( 328 display_layout); 329 330 controller()->SetEnabled(true); 331 ASSERT_TRUE(controller()->IsEnabled()); 332 ASSERT_FALSE(controller()->IsRevealed()); 333 334 ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows(); 335 ASSERT_EQ(root_windows[0], 336 top_container()->GetWidget()->GetNativeWindow()->GetRootWindow()); 337 338 gfx::Rect primary_root_window_bounds_in_screen = 339 root_windows[0]->GetBoundsInScreen(); 340 // Do not set |x| to the root window's x position because the display's 341 // corners have special behavior. 342 int x = primary_root_window_bounds_in_screen.x() + 10; 343 // The y position of the top edge of the primary display. 344 int y_top_edge = primary_root_window_bounds_in_screen.y(); 345 346 aura::test::EventGenerator& event_generator(GetEventGenerator()); 347 348 // Moving right below the top edge starts the hover timer running. We 349 // cannot use MoveMouse() because MoveMouse() stops the timer if it started 350 // running. 351 event_generator.MoveMouseTo(x, y_top_edge + 1); 352 EXPECT_TRUE(top_edge_hover_timer_running()); 353 EXPECT_EQ(y_top_edge + 1, 354 aura::Env::GetInstance()->last_mouse_location().y()); 355 356 // The timer should continue running if the user moves the mouse to the top 357 // edge even though the mouse is warped to the secondary display. 358 event_generator.MoveMouseTo(x, y_top_edge); 359 EXPECT_TRUE(top_edge_hover_timer_running()); 360 EXPECT_NE(y_top_edge, 361 aura::Env::GetInstance()->last_mouse_location().y()); 362 363 // The timer should continue running if the user overshoots the top edge 364 // a bit. 365 event_generator.MoveMouseTo(x, y_top_edge - 2); 366 EXPECT_TRUE(top_edge_hover_timer_running()); 367 368 // The timer should stop running if the user overshoots the top edge by 369 // a lot. 370 event_generator.MoveMouseTo(x, y_top_edge - 20); 371 EXPECT_FALSE(top_edge_hover_timer_running()); 372 373 // The timer should not start if the user moves the mouse to the bottom of the 374 // secondary display without crossing the top edge first. 375 event_generator.MoveMouseTo(x, y_top_edge - 2); 376 377 // Reveal the top-of-window views by overshooting the top edge slightly. 378 event_generator.MoveMouseTo(x, y_top_edge + 1); 379 // MoveMouse() runs the timer task. 380 MoveMouse(x, y_top_edge - 2); 381 EXPECT_TRUE(controller()->IsRevealed()); 382 383 // The top-of-window views should stay revealed if the user moves the mouse 384 // around in the bottom region of the secondary display. 385 event_generator.MoveMouseTo(x + 10, y_top_edge - 3); 386 EXPECT_TRUE(controller()->IsRevealed()); 387 388 // The top-of-window views should hide if the user moves the mouse away from 389 // the bottom region of the secondary display. 390 event_generator.MoveMouseTo(x, y_top_edge - 20); 391 EXPECT_FALSE(controller()->IsRevealed()); 392} 393 394// Test that hovering the mouse over the find bar does not end a reveal. 395TEST_F(ImmersiveModeControllerAshTest, FindBar) { 396 // Set up initial state. 397 controller()->SetEnabled(true); 398 ASSERT_TRUE(controller()->IsEnabled()); 399 ASSERT_FALSE(controller()->IsRevealed()); 400 401 // Compute the find bar bounds relative to TopContainerView. The find 402 // bar is aligned with the bottom right of the TopContainerView. 403 gfx::Rect find_bar_bounds(top_container()->bounds().right() - 100, 404 top_container()->bounds().bottom(), 405 100, 406 50); 407 408 gfx::Point find_bar_position_in_screen = find_bar_bounds.origin(); 409 views::View::ConvertPointToScreen(top_container(), 410 &find_bar_position_in_screen); 411 gfx::Rect find_bar_bounds_in_screen(find_bar_position_in_screen, 412 find_bar_bounds.size()); 413 controller()->OnFindBarVisibleBoundsChanged(find_bar_bounds_in_screen); 414 415 // Moving the mouse over the find bar does not end the reveal. 416 gfx::Point over_find_bar(find_bar_bounds.x() + 25, find_bar_bounds.y() + 25); 417 AttemptReveal(MODALITY_MOUSE); 418 EXPECT_TRUE(controller()->IsRevealed()); 419 MoveMouse(over_find_bar.x(), over_find_bar.y()); 420 EXPECT_TRUE(controller()->IsRevealed()); 421 422 // Moving the mouse off of the find bar horizontally ends the reveal. 423 MoveMouse(find_bar_bounds.x() - 25, find_bar_bounds.y() + 25); 424 EXPECT_FALSE(controller()->IsRevealed()); 425 426 // Moving the mouse off of the find bar vertically ends the reveal. 427 AttemptReveal(MODALITY_MOUSE); 428 EXPECT_TRUE(controller()->IsRevealed()); 429 MoveMouse(find_bar_bounds.x() + 25, find_bar_bounds.bottom() + 25); 430 431 // Similar to the TopContainerView, moving the mouse slightly off vertically 432 // of the find bar does not end the reveal. 433 AttemptReveal(MODALITY_MOUSE); 434 MoveMouse(find_bar_bounds.x() + 25, find_bar_bounds.bottom() + 1); 435 EXPECT_TRUE(controller()->IsRevealed()); 436 437 // Similar to the TopContainerView, clicking the mouse even slightly off of 438 // the find bar ends the reveal. 439 GetEventGenerator().ClickLeftButton(); 440 EXPECT_FALSE(controller()->IsRevealed()); 441 442 // Set the find bar bounds to empty. Hovering over the position previously 443 // occupied by the find bar, |over_find_bar|, should end the reveal. 444 controller()->OnFindBarVisibleBoundsChanged(gfx::Rect()); 445 AttemptReveal(MODALITY_MOUSE); 446 MoveMouse(over_find_bar.x(), over_find_bar.y()); 447 EXPECT_FALSE(controller()->IsRevealed()); 448} 449 450// Test revealing the top-of-window views using one modality and ending 451// the reveal via another. For instance, initiating the reveal via a SWIPE_OPEN 452// edge gesture, switching to using the mouse and ending the reveal by moving 453// the mouse off of the top-of-window views. 454TEST_F(ImmersiveModeControllerAshTest, DifferentModalityEnterExit) { 455 controller()->SetEnabled(true); 456 EXPECT_TRUE(controller()->IsEnabled()); 457 EXPECT_FALSE(controller()->IsRevealed()); 458 459 // Initiate reveal via gesture, end reveal via mouse. 460 AttemptReveal(MODALITY_GESTURE); 461 EXPECT_TRUE(controller()->IsRevealed()); 462 MoveMouse(1, 1); 463 EXPECT_TRUE(controller()->IsRevealed()); 464 AttemptUnreveal(MODALITY_MOUSE); 465 EXPECT_FALSE(controller()->IsRevealed()); 466 467 // Initiate reveal via gesture, end reveal via touch. 468 AttemptReveal(MODALITY_GESTURE); 469 EXPECT_TRUE(controller()->IsRevealed()); 470 AttemptUnreveal(MODALITY_TOUCH); 471 EXPECT_FALSE(controller()->IsRevealed()); 472 473 // Initiate reveal via mouse, end reveal via gesture. 474 AttemptReveal(MODALITY_MOUSE); 475 EXPECT_TRUE(controller()->IsRevealed()); 476 AttemptUnreveal(MODALITY_GESTURE); 477 EXPECT_FALSE(controller()->IsRevealed()); 478 479 // Initiate reveal via mouse, end reveal via touch. 480 AttemptReveal(MODALITY_MOUSE); 481 EXPECT_TRUE(controller()->IsRevealed()); 482 AttemptUnreveal(MODALITY_TOUCH); 483 EXPECT_FALSE(controller()->IsRevealed()); 484} 485 486// Test when the SWIPE_CLOSE edge gesture closes the top-of-window views. 487TEST_F(ImmersiveModeControllerAshTest, EndRevealViaGesture) { 488 controller()->SetEnabled(true); 489 EXPECT_TRUE(controller()->IsEnabled()); 490 EXPECT_FALSE(controller()->IsRevealed()); 491 492 // A gesture should be able to close the top-of-window views when 493 // top-of-window views have focus. 494 AttemptReveal(MODALITY_MOUSE); 495 top_container()->RequestFocus(); 496 EXPECT_TRUE(controller()->IsRevealed()); 497 AttemptUnreveal(MODALITY_GESTURE); 498 EXPECT_FALSE(controller()->IsRevealed()); 499 top_container()->GetFocusManager()->ClearFocus(); 500 501 // If some other code is holding onto a lock, a gesture should not be able to 502 // end the reveal. 503 AttemptReveal(MODALITY_MOUSE); 504 scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock( 505 ImmersiveModeController::ANIMATE_REVEAL_NO)); 506 EXPECT_TRUE(controller()->IsRevealed()); 507 AttemptUnreveal(MODALITY_GESTURE); 508 EXPECT_TRUE(controller()->IsRevealed()); 509 lock.reset(); 510 EXPECT_FALSE(controller()->IsRevealed()); 511} 512 513// Do not test under windows because focus testing is not reliable on 514// Windows. (crbug.com/79493) 515#if !defined(OS_WIN) 516 517// Test how focus and activation affects whether the top-of-window views are 518// revealed. 519TEST_F(ImmersiveModeControllerAshTest, Focus) { 520 // Add views to the view hierarchy which we will focus and unfocus during the 521 // test. 522 views::View* child_view = new views::View(); 523 child_view->SetBounds(0, 0, 10, 10); 524 child_view->set_focusable(true); 525 top_container()->AddChildView(child_view); 526 views::View* unrelated_view = new views::View(); 527 unrelated_view->SetBounds(0, 100, 10, 10); 528 unrelated_view->set_focusable(true); 529 top_container()->parent()->AddChildView(unrelated_view); 530 views::FocusManager* focus_manager = 531 top_container()->GetWidget()->GetFocusManager(); 532 533 controller()->SetEnabled(true); 534 535 // 1) Test that the top-of-window views stay revealed as long as either a 536 // |child_view| has focus or the mouse is hovered above the top-of-window 537 // views. 538 AttemptReveal(MODALITY_MOUSE); 539 child_view->RequestFocus(); 540 focus_manager->ClearFocus(); 541 EXPECT_TRUE(controller()->IsRevealed()); 542 child_view->RequestFocus(); 543 SetHovered(false); 544 EXPECT_TRUE(controller()->IsRevealed()); 545 focus_manager->ClearFocus(); 546 EXPECT_FALSE(controller()->IsRevealed()); 547 548 // 2) Test that focusing |unrelated_view| hides the top-of-window views. 549 // Note: In this test we can cheat and trigger a reveal via focus because 550 // the top container does not hide when the top-of-window views are not 551 // revealed. 552 child_view->RequestFocus(); 553 EXPECT_TRUE(controller()->IsRevealed()); 554 unrelated_view->RequestFocus(); 555 EXPECT_FALSE(controller()->IsRevealed()); 556 557 // 3) Test that a loss of focus of |child_view| to |unrelated_view| 558 // while immersive mode is disabled is properly registered. 559 child_view->RequestFocus(); 560 EXPECT_TRUE(controller()->IsRevealed()); 561 controller()->SetEnabled(false); 562 EXPECT_FALSE(controller()->IsRevealed()); 563 unrelated_view->RequestFocus(); 564 controller()->SetEnabled(true); 565 EXPECT_FALSE(controller()->IsRevealed()); 566 567 // Repeat test but with a revealed lock acquired when immersive mode is 568 // disabled because the code path is different. 569 child_view->RequestFocus(); 570 EXPECT_TRUE(controller()->IsRevealed()); 571 controller()->SetEnabled(false); 572 scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock( 573 ImmersiveModeController::ANIMATE_REVEAL_NO)); 574 EXPECT_FALSE(controller()->IsRevealed()); 575 unrelated_view->RequestFocus(); 576 controller()->SetEnabled(true); 577 EXPECT_TRUE(controller()->IsRevealed()); 578 lock.reset(); 579 EXPECT_FALSE(controller()->IsRevealed()); 580} 581 582// Test how activation affects whether the top-of-window views are revealed. 583// The behavior when a bubble is activated is tested in 584// ImmersiveModeControllerAshTest.Bubbles. 585TEST_F(ImmersiveModeControllerAshTest, Activation) { 586 views::Widget* top_container_widget = top_container()->GetWidget(); 587 588 controller()->SetEnabled(true); 589 ASSERT_FALSE(controller()->IsRevealed()); 590 591 // 1) Test that a transient window which is not a bubble does not trigger a 592 // reveal but does keep the top-of-window views revealed if they are already 593 // revealed. 594 views::Widget::InitParams transient_params; 595 transient_params.ownership = 596 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 597 transient_params.parent = top_container_widget->GetNativeView(); 598 transient_params.bounds = gfx::Rect(0, 0, 100, 100); 599 scoped_ptr<views::Widget> transient_widget(new views::Widget()); 600 transient_widget->Init(transient_params); 601 transient_widget->Show(); 602 603 EXPECT_FALSE(controller()->IsRevealed()); 604 top_container_widget->Activate(); 605 AttemptReveal(MODALITY_MOUSE); 606 EXPECT_TRUE(controller()->IsRevealed()); 607 transient_widget->Activate(); 608 SetHovered(false); 609 EXPECT_TRUE(controller()->IsRevealed()); 610 transient_widget.reset(); 611 EXPECT_FALSE(controller()->IsRevealed()); 612 613 // 2) Test that activating a non-transient window ends the reveal if any. 614 views::Widget::InitParams non_transient_params; 615 non_transient_params.ownership = 616 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 617 non_transient_params.context = top_container_widget->GetNativeView(); 618 non_transient_params.bounds = gfx::Rect(0, 0, 100, 100); 619 scoped_ptr<views::Widget> non_transient_widget(new views::Widget()); 620 non_transient_widget->Init(non_transient_params); 621 non_transient_widget->Show(); 622 623 EXPECT_FALSE(controller()->IsRevealed()); 624 top_container_widget->Activate(); 625 AttemptReveal(MODALITY_MOUSE); 626 EXPECT_TRUE(controller()->IsRevealed()); 627 non_transient_widget->Activate(); 628 EXPECT_FALSE(controller()->IsRevealed()); 629} 630 631// Test how bubbles affect whether the top-of-window views are revealed. 632TEST_F(ImmersiveModeControllerAshTest, Bubbles) { 633 scoped_ptr<ImmersiveRevealedLock> revealed_lock; 634 views::Widget* top_container_widget = top_container()->GetWidget(); 635 636 // Add views to the view hierarchy to which we will anchor bubbles. 637 views::View* child_view = new views::View(); 638 child_view->SetBounds(0, 0, 10, 10); 639 top_container()->AddChildView(child_view); 640 views::View* unrelated_view = new views::View(); 641 unrelated_view->SetBounds(0, 100, 10, 10); 642 top_container()->parent()->AddChildView(unrelated_view); 643 644 controller()->SetEnabled(true); 645 ASSERT_FALSE(controller()->IsRevealed()); 646 647 // 1) Test that a bubble anchored to a child of the top container triggers 648 // a reveal and keeps the top-of-window views revealed for the duration of 649 // its visibility. 650 views::Widget* bubble_widget1(views::BubbleDelegateView::CreateBubble( 651 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE))); 652 bubble_widget1->Show(); 653 EXPECT_TRUE(controller()->IsRevealed()); 654 655 // Activating |top_container_widget| will close |bubble_widget1|. 656 top_container_widget->Activate(); 657 AttemptReveal(MODALITY_MOUSE); 658 revealed_lock.reset(controller()->GetRevealedLock( 659 ImmersiveModeController::ANIMATE_REVEAL_NO)); 660 EXPECT_TRUE(controller()->IsRevealed()); 661 662 views::Widget* bubble_widget2 = views::BubbleDelegateView::CreateBubble( 663 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE)); 664 bubble_widget2->Show(); 665 EXPECT_TRUE(controller()->IsRevealed()); 666 revealed_lock.reset(); 667 SetHovered(false); 668 EXPECT_TRUE(controller()->IsRevealed()); 669 bubble_widget2->Close(); 670 EXPECT_FALSE(controller()->IsRevealed()); 671 672 // 2) Test that transitioning from keeping the top-of-window views revealed 673 // because of a bubble to keeping the top-of-window views revealed because of 674 // mouse hover by activating |top_container_widget| works. 675 views::Widget* bubble_widget3 = views::BubbleDelegateView::CreateBubble( 676 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE)); 677 bubble_widget3->Show(); 678 SetHovered(true); 679 EXPECT_TRUE(controller()->IsRevealed()); 680 681 SetAnimationsDisabled(false); 682 // Activating |top_container_widget| will close |bubble_widget3|. 683 top_container_widget->Activate(); 684 SetAnimationsDisabled(true); 685 EXPECT_TRUE(controller()->IsRevealed()); 686 687 // 3) Test that the top-of-window views stay revealed as long as at least one 688 // bubble anchored to a child of the top container is visible. 689 SetHovered(false); 690 EXPECT_FALSE(controller()->IsRevealed()); 691 692 views::BubbleDelegateView* bubble_delegate4(new views::BubbleDelegateView( 693 child_view, views::BubbleBorder::NONE)); 694 bubble_delegate4->set_use_focusless(true); 695 views::Widget* bubble_widget4(views::BubbleDelegateView::CreateBubble( 696 bubble_delegate4)); 697 bubble_widget4->Show(); 698 699 views::BubbleDelegateView* bubble_delegate5(new views::BubbleDelegateView( 700 child_view, views::BubbleBorder::NONE)); 701 bubble_delegate5->set_use_focusless(true); 702 views::Widget* bubble_widget5(views::BubbleDelegateView::CreateBubble( 703 bubble_delegate5)); 704 bubble_widget5->Show(); 705 706 EXPECT_TRUE(controller()->IsRevealed()); 707 bubble_widget4->Hide(); 708 EXPECT_TRUE(controller()->IsRevealed()); 709 bubble_widget5->Hide(); 710 EXPECT_FALSE(controller()->IsRevealed()); 711 bubble_widget5->Show(); 712 EXPECT_TRUE(controller()->IsRevealed()); 713 714 // 4) Test that visibility changes which occur while immersive fullscreen is 715 // disabled are handled upon reenabling immersive fullscreen. 716 controller()->SetEnabled(false); 717 bubble_widget5->Hide(); 718 controller()->SetEnabled(true); 719 EXPECT_FALSE(controller()->IsRevealed()); 720 721 // We do not need |bubble_widget4| or |bubble_widget5| anymore, close them. 722 bubble_widget4->Close(); 723 bubble_widget5->Close(); 724 725 // 5) Test that a bubble added while immersive fullscreen is disabled is 726 // handled upon reenabling immersive fullscreen. 727 controller()->SetEnabled(false); 728 729 views::Widget* bubble_widget6 = views::BubbleDelegateView::CreateBubble( 730 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE)); 731 bubble_widget6->Show(); 732 733 controller()->SetEnabled(true); 734 EXPECT_TRUE(controller()->IsRevealed()); 735 736 bubble_widget6->Close(); 737 738 // 6) Test that a bubble which is not anchored to a child of the 739 // TopContainerView does not trigger a reveal or keep the 740 // top-of-window views revealed if they are already revealed. 741 views::Widget* bubble_widget7 = views::BubbleDelegateView::CreateBubble( 742 new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE)); 743 bubble_widget7->Show(); 744 EXPECT_FALSE(controller()->IsRevealed()); 745 746 // Activating |top_container_widget| will close |bubble_widget6|. 747 top_container_widget->Activate(); 748 AttemptReveal(MODALITY_MOUSE); 749 EXPECT_TRUE(controller()->IsRevealed()); 750 751 views::Widget* bubble_widget8 = views::BubbleDelegateView::CreateBubble( 752 new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE)); 753 bubble_widget8->Show(); 754 SetHovered(false); 755 EXPECT_FALSE(controller()->IsRevealed()); 756 bubble_widget8->Close(); 757} 758 759#endif // defined(OS_WIN) 760 761#endif // defined(OS_CHROMEOS) 762