immersive_mode_controller_ash_unittest.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/test/ash_test_base.h" 8#include "chrome/browser/ui/immersive_fullscreen_configuration.h" 9#include "ui/aura/client/cursor_client.h" 10#include "ui/aura/root_window.h" 11#include "ui/aura/test/event_generator.h" 12#include "ui/aura/window.h" 13 14// For now, immersive fullscreen is Chrome OS only. 15#if defined(OS_CHROMEOS) 16 17///////////////////////////////////////////////////////////////////////////// 18 19class MockImmersiveModeControllerDelegate 20 : public ImmersiveModeController::Delegate { 21 public: 22 MockImmersiveModeControllerDelegate() : immersive_style_(false) {} 23 virtual ~MockImmersiveModeControllerDelegate() {} 24 25 bool immersive_style() const { return immersive_style_; } 26 27 // ImmersiveModeController::Delegate overrides: 28 virtual BookmarkBarView* GetBookmarkBar() OVERRIDE { return NULL; } 29 virtual FullscreenController* GetFullscreenController() OVERRIDE { 30 return NULL; 31 } 32 virtual void FullscreenStateChanged() OVERRIDE {} 33 virtual void SetImmersiveStyle(bool immersive) OVERRIDE { 34 immersive_style_ = immersive; 35 } 36 37 private: 38 bool immersive_style_; 39 40 DISALLOW_COPY_AND_ASSIGN(MockImmersiveModeControllerDelegate); 41}; 42 43///////////////////////////////////////////////////////////////////////////// 44 45class ImmersiveModeControllerAshTest : public ash::test::AshTestBase { 46 public: 47 enum Modality { 48 MODALITY_MOUSE, 49 MODALITY_TOUCH, 50 MODALITY_GESTURE 51 }; 52 53 ImmersiveModeControllerAshTest() : widget_(NULL), top_container_(NULL) {} 54 virtual ~ImmersiveModeControllerAshTest() {} 55 56 ImmersiveModeControllerAsh* controller() { return controller_.get(); } 57 views::View* top_container() { return top_container_; } 58 MockImmersiveModeControllerDelegate* delegate() { return delegate_.get(); } 59 60 aura::test::EventGenerator* event_generator() { 61 return event_generator_.get(); 62 } 63 64 // Access to private data from the controller. 65 bool top_edge_hover_timer_running() const { 66 return controller_->top_edge_hover_timer_.IsRunning(); 67 } 68 int mouse_x_when_hit_top() const { 69 return controller_->mouse_x_when_hit_top_; 70 } 71 72 // ash::test::AshTestBase overrides: 73 virtual void SetUp() OVERRIDE { 74 ash::test::AshTestBase::SetUp(); 75 76 ImmersiveFullscreenConfiguration::EnableImmersiveFullscreenForTest(); 77 ASSERT_TRUE(ImmersiveFullscreenConfiguration::UseImmersiveFullscreen()); 78 79 controller_.reset(new ImmersiveModeControllerAsh); 80 delegate_.reset(new MockImmersiveModeControllerDelegate); 81 82 event_generator_.reset(new aura::test::EventGenerator(CurrentContext())); 83 84 widget_ = new views::Widget(); 85 views::Widget::InitParams params; 86 params.context = CurrentContext(); 87 params.bounds = gfx::Rect(0, 0, 500, 500); 88 widget_->Init(params); 89 widget_->Show(); 90 91 top_container_ = new views::View(); 92 top_container_->SetBounds(0, 0, 500, 100); 93 top_container_->set_focusable(true); 94 95 widget_->GetContentsView()->AddChildView(top_container_); 96 97 controller_->Init(delegate_.get(), widget_, top_container_); 98 controller_->DisableAnimationsForTest(); 99 } 100 101 // Attempt to reveal the top-of-window views via |modality|. 102 // The top-of-window views can only be revealed via mouse hover or a gesture. 103 void AttemptReveal(Modality modality) { 104 ASSERT_NE(modality, MODALITY_TOUCH); 105 AttemptRevealStateChange(true, modality); 106 } 107 108 // Attempt to unreveal the top-of-window views via |modality|. The 109 // top-of-window views can be unrevealed via any modality. 110 void AttemptUnreveal(Modality modality) { 111 AttemptRevealStateChange(false, modality); 112 } 113 114 // Move the mouse to the given coordinates. The coordinates should be in 115 // |top_container_| coordinates. 116 void MoveMouse(int x, int y) { 117 // Luckily, |top_container_| is at the top left of the root window so the 118 // provided coordinates are already in the coordinates of the root window. 119 event_generator_->MoveMouseTo(x, y); 120 121 // If the top edge timer started running as a result of the mouse move, run 122 // the task which occurs after the timer delay. This reveals the 123 // top-of-window views synchronously if the mouse is hovered at the top of 124 // the screen. 125 if (controller()->top_edge_hover_timer_.IsRunning()) { 126 controller()->top_edge_hover_timer_.user_task().Run(); 127 controller()->top_edge_hover_timer_.Stop(); 128 } 129 } 130 131 private: 132 // Attempt to change the revealed state to |revealed| via |modality|. 133 void AttemptRevealStateChange(bool revealed, Modality modality) { 134 // Compute the event position in |top_container_| coordinates. 135 gfx::Point event_position(0, revealed ? 0 : top_container_->height() + 100); 136 switch (modality) { 137 case MODALITY_MOUSE: { 138 MoveMouse(event_position.x(), event_position.y()); 139 break; 140 } 141 case MODALITY_TOUCH: { 142 // Luckily, |top_container_| is at the top left of the root window so 143 // |event_position| is already in the coordinates of the root window. 144 event_generator_->MoveTouch(event_position); 145 event_generator_->PressTouch(); 146 event_generator_->ReleaseTouch(); 147 break; 148 } 149 case MODALITY_GESTURE: { 150 aura::client::GetCursorClient(CurrentContext())->DisableMouseEvents(); 151 ImmersiveModeControllerAsh::SwipeType swipe_type = revealed ? 152 ImmersiveModeControllerAsh::SWIPE_OPEN : 153 ImmersiveModeControllerAsh::SWIPE_CLOSE; 154 controller_->UpdateRevealedLocksForSwipe(swipe_type); 155 break; 156 } 157 } 158 } 159 160 scoped_ptr<ImmersiveModeControllerAsh> controller_; 161 scoped_ptr<MockImmersiveModeControllerDelegate> delegate_; 162 views::Widget* widget_; // Owned by the native widget. 163 views::View* top_container_; // Owned by |root_view_|. 164 scoped_ptr<aura::test::EventGenerator> event_generator_; 165 166 DISALLOW_COPY_AND_ASSIGN(ImmersiveModeControllerAshTest); 167}; 168 169// Test of initial state and basic functionality. 170TEST_F(ImmersiveModeControllerAshTest, ImmersiveModeControllerAsh) { 171 // Initial state. 172 EXPECT_FALSE(controller()->IsEnabled()); 173 EXPECT_FALSE(controller()->ShouldHideTopViews()); 174 EXPECT_FALSE(controller()->IsRevealed()); 175 EXPECT_FALSE(delegate()->immersive_style()); 176 177 // Enabling hides the top views. 178 controller()->SetEnabled(true); 179 EXPECT_TRUE(controller()->IsEnabled()); 180 EXPECT_FALSE(controller()->IsRevealed()); 181 EXPECT_TRUE(controller()->ShouldHideTopViews()); 182 EXPECT_FALSE(controller()->ShouldHideTabIndicators()); 183 EXPECT_TRUE(delegate()->immersive_style()); 184 185 // Revealing shows the top views. 186 AttemptReveal(MODALITY_MOUSE); 187 EXPECT_TRUE(controller()->IsRevealed()); 188 EXPECT_FALSE(controller()->ShouldHideTopViews()); 189 // Tabs are painting in the normal style during a reveal. 190 EXPECT_FALSE(delegate()->immersive_style()); 191} 192 193// Test mouse event processing for top-of-screen reveal triggering. 194TEST_F(ImmersiveModeControllerAshTest, OnMouseEvent) { 195 // Set up initial state. 196 controller()->SetEnabled(true); 197 ASSERT_TRUE(controller()->IsEnabled()); 198 ASSERT_FALSE(controller()->IsRevealed()); 199 200 // Mouse wheel event does nothing. 201 ui::MouseEvent wheel( 202 ui::ET_MOUSEWHEEL, gfx::Point(), gfx::Point(), ui::EF_NONE); 203 event_generator()->Dispatch(&wheel); 204 EXPECT_FALSE(top_edge_hover_timer_running()); 205 206 // Move to top edge of screen starts hover timer running. We cannot use 207 // MoveMouse() because MoveMouse() stops the timer if it started running. 208 event_generator()->MoveMouseTo(100, 0); 209 EXPECT_TRUE(top_edge_hover_timer_running()); 210 EXPECT_EQ(100, mouse_x_when_hit_top()); 211 212 // Moving off the top edge stops it. 213 event_generator()->MoveMouseTo(100, 1); 214 EXPECT_FALSE(top_edge_hover_timer_running()); 215 216 // Moving back to the top starts the timer again. 217 event_generator()->MoveMouseTo(100, 0); 218 EXPECT_TRUE(top_edge_hover_timer_running()); 219 EXPECT_EQ(100, mouse_x_when_hit_top()); 220 221 // Slight move to the right keeps the timer running for the same hit point. 222 event_generator()->MoveMouseTo(101, 0); 223 EXPECT_TRUE(top_edge_hover_timer_running()); 224 EXPECT_EQ(100, mouse_x_when_hit_top()); 225 226 // Moving back to the left also keeps the timer running. 227 event_generator()->MoveMouseTo(100, 0); 228 EXPECT_TRUE(top_edge_hover_timer_running()); 229 EXPECT_EQ(100, mouse_x_when_hit_top()); 230 231 // Large move right restarts the timer (so it is still running) and considers 232 // this a new hit at the top. 233 event_generator()->MoveMouseTo(499, 0); 234 EXPECT_TRUE(top_edge_hover_timer_running()); 235 EXPECT_EQ(499, mouse_x_when_hit_top()); 236 237 // Moving off the top edge horizontally stops the timer. 238 EXPECT_GT(CurrentContext()->bounds().width(), top_container()->width()); 239 EXPECT_EQ(500, top_container()->width()); 240 event_generator()->MoveMouseTo(500, 0); 241 EXPECT_FALSE(top_edge_hover_timer_running()); 242 243 // Once revealed, a move just a little below the top container doesn't end a 244 // reveal. 245 AttemptReveal(MODALITY_MOUSE); 246 event_generator()->MoveMouseTo(0, top_container()->height() + 1); 247 EXPECT_TRUE(controller()->IsRevealed()); 248 249 // Once revealed, clicking just below the top container ends the reveal. 250 event_generator()->ClickLeftButton(); 251 EXPECT_FALSE(controller()->IsRevealed()); 252 253 // Moving a lot below the top container ends a reveal. 254 AttemptReveal(MODALITY_MOUSE); 255 EXPECT_TRUE(controller()->IsRevealed()); 256 event_generator()->MoveMouseTo(0, top_container()->height() + 50); 257 EXPECT_FALSE(controller()->IsRevealed()); 258 259 // The mouse position cannot cause a reveal when TopContainerView's widget 260 // has capture. 261 views::Widget* widget = top_container()->GetWidget(); 262 widget->SetCapture(top_container()); 263 AttemptReveal(MODALITY_MOUSE); 264 EXPECT_FALSE(controller()->IsRevealed()); 265 widget->ReleaseCapture(); 266 267 // The mouse position cannot end the reveal while TopContainerView's widget 268 // has capture. 269 AttemptReveal(MODALITY_MOUSE); 270 EXPECT_TRUE(controller()->IsRevealed()); 271 widget->SetCapture(top_container()); 272 event_generator()->MoveMouseTo(0, top_container()->height() + 51); 273 EXPECT_TRUE(controller()->IsRevealed()); 274 275 // Releasing capture should end the reveal. 276 widget->ReleaseCapture(); 277 EXPECT_FALSE(controller()->IsRevealed()); 278} 279 280// Test that hovering the mouse over the find bar does not end a reveal. 281TEST_F(ImmersiveModeControllerAshTest, FindBar) { 282 // Set up initial state. 283 controller()->SetEnabled(true); 284 ASSERT_TRUE(controller()->IsEnabled()); 285 ASSERT_FALSE(controller()->IsRevealed()); 286 287 // Compute the find bar bounds relative to TopContainerView. The find 288 // bar is aligned with the bottom right of the TopContainerView. 289 gfx::Rect find_bar_bounds(top_container()->bounds().right() - 100, 290 top_container()->bounds().bottom(), 291 100, 292 50); 293 294 gfx::Point find_bar_position_in_screen = find_bar_bounds.origin(); 295 views::View::ConvertPointToScreen(top_container(), 296 &find_bar_position_in_screen); 297 gfx::Rect find_bar_bounds_in_screen(find_bar_position_in_screen, 298 find_bar_bounds.size()); 299 controller()->OnFindBarVisibleBoundsChanged(find_bar_bounds_in_screen); 300 301 // Moving the mouse over the find bar does not end the reveal. 302 gfx::Point over_find_bar(find_bar_bounds.x() + 25, find_bar_bounds.y() + 25); 303 AttemptReveal(MODALITY_MOUSE); 304 EXPECT_TRUE(controller()->IsRevealed()); 305 MoveMouse(over_find_bar.x(), over_find_bar.y()); 306 EXPECT_TRUE(controller()->IsRevealed()); 307 308 // Moving the mouse off of the find bar horizontally ends the reveal. 309 MoveMouse(find_bar_bounds.x() - 25, find_bar_bounds.y() + 25); 310 EXPECT_FALSE(controller()->IsRevealed()); 311 312 // Moving the mouse off of the find bar vertically ends the reveal. 313 AttemptReveal(MODALITY_MOUSE); 314 EXPECT_TRUE(controller()->IsRevealed()); 315 MoveMouse(find_bar_bounds.x() + 25, find_bar_bounds.bottom() + 25); 316 317 // Similar to the TopContainerView, moving the mouse slightly off vertically 318 // of the find bar does not end the reveal. 319 AttemptReveal(MODALITY_MOUSE); 320 MoveMouse(find_bar_bounds.x() + 25, find_bar_bounds.bottom() + 1); 321 EXPECT_TRUE(controller()->IsRevealed()); 322 323 // Similar to the TopContainerView, clicking the mouse even slightly off of 324 // the find bar ends the reveal. 325 event_generator()->ClickLeftButton(); 326 EXPECT_FALSE(controller()->IsRevealed()); 327 328 // Set the find bar bounds to empty. Hovering over the position previously 329 // occupied by the find bar, |over_find_bar|, should end the reveal. 330 controller()->OnFindBarVisibleBoundsChanged(gfx::Rect()); 331 AttemptReveal(MODALITY_MOUSE); 332 MoveMouse(over_find_bar.x(), over_find_bar.y()); 333 EXPECT_FALSE(controller()->IsRevealed()); 334} 335 336// Test revealing the top-of-window views using one modality and ending 337// the reveal via another. For instance, initiating the reveal via a SWIPE_OPEN 338// edge gesture, switching to using the mouse and ending the reveal by moving 339// the mouse off of the top-of-window views. 340TEST_F(ImmersiveModeControllerAshTest, DifferentModalityEnterExit) { 341 controller()->SetEnabled(true); 342 EXPECT_TRUE(controller()->IsEnabled()); 343 EXPECT_FALSE(controller()->IsRevealed()); 344 345 // Initiate reveal via gesture, end reveal via mouse. 346 AttemptReveal(MODALITY_GESTURE); 347 EXPECT_TRUE(controller()->IsRevealed()); 348 MoveMouse(1, 1); 349 EXPECT_TRUE(controller()->IsRevealed()); 350 AttemptUnreveal(MODALITY_MOUSE); 351 EXPECT_FALSE(controller()->IsRevealed()); 352 353 // Initiate reveal via gesture, end reveal via touch. 354 AttemptReveal(MODALITY_GESTURE); 355 EXPECT_TRUE(controller()->IsRevealed()); 356 AttemptUnreveal(MODALITY_TOUCH); 357 EXPECT_FALSE(controller()->IsRevealed()); 358 359 // Initiate reveal via mouse, end reveal via gesture. 360 AttemptReveal(MODALITY_MOUSE); 361 EXPECT_TRUE(controller()->IsRevealed()); 362 AttemptUnreveal(MODALITY_GESTURE); 363 EXPECT_FALSE(controller()->IsRevealed()); 364 365 // Initiate reveal via mouse, end reveal via touch. 366 AttemptReveal(MODALITY_MOUSE); 367 EXPECT_TRUE(controller()->IsRevealed()); 368 AttemptUnreveal(MODALITY_TOUCH); 369 EXPECT_FALSE(controller()->IsRevealed()); 370} 371 372// Test when the SWIPE_CLOSE edge gesture closes the top-of-window views. 373TEST_F(ImmersiveModeControllerAshTest, EndRevealViaGesture) { 374 controller()->SetEnabled(true); 375 EXPECT_TRUE(controller()->IsEnabled()); 376 EXPECT_FALSE(controller()->IsRevealed()); 377 378 // A gesture should be able to close the top-of-window views when 379 // top-of-window views have focus. 380 AttemptReveal(MODALITY_MOUSE); 381 top_container()->RequestFocus(); 382 EXPECT_TRUE(controller()->IsRevealed()); 383 AttemptUnreveal(MODALITY_GESTURE); 384 EXPECT_FALSE(controller()->IsRevealed()); 385 top_container()->GetFocusManager()->ClearFocus(); 386 387 // If some other code is holding onto a lock, a gesture should not be able to 388 // end the reveal. 389 AttemptReveal(MODALITY_MOUSE); 390 scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock( 391 ImmersiveModeController::ANIMATE_REVEAL_NO)); 392 EXPECT_TRUE(controller()->IsRevealed()); 393 AttemptUnreveal(MODALITY_GESTURE); 394 EXPECT_TRUE(controller()->IsRevealed()); 395 lock.reset(); 396 EXPECT_FALSE(controller()->IsRevealed()); 397} 398 399#endif // defined(OS_CHROMEOS) 400