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/ash_switches.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 "base/command_line.h" 14 #include "chrome/app/chrome_command_ids.h" 15 #include "chrome/browser/ui/browser_commands.h" 16 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h" 17 #include "chrome/browser/ui/fullscreen/fullscreen_controller_test.h" 18 #include "chrome/browser/ui/views/frame/browser_view.h" 19 #include "chrome/browser/ui/views/frame/test_with_browser_view.h" 20 #include "chrome/browser/ui/views/frame/top_container_view.h" 21 #include "chrome/browser/ui/views/tabs/tab_strip.h" 22 #include "chrome/browser/ui/views/toolbar/toolbar_view.h" 23 #include "ui/aura/window.h" 24 #include "ui/views/controls/webview/webview.h" 25 26 class ImmersiveModeControllerAshTest : public TestWithBrowserView { 27 public: 28 ImmersiveModeControllerAshTest() 29 : TestWithBrowserView(Browser::TYPE_TABBED, 30 chrome::HOST_DESKTOP_TYPE_ASH, 31 false) { 32 } 33 ImmersiveModeControllerAshTest( 34 Browser::Type browser_type, 35 chrome::HostDesktopType host_desktop_type, 36 bool hosted_app) 37 : TestWithBrowserView(browser_type, 38 host_desktop_type, 39 hosted_app) { 40 } 41 virtual ~ImmersiveModeControllerAshTest() {} 42 43 // TestWithBrowserView override: 44 virtual void SetUp() OVERRIDE { 45 TestWithBrowserView::SetUp(); 46 47 browser()->window()->Show(); 48 49 controller_ = browser_view()->immersive_mode_controller(); 50 controller_->SetupForTest(); 51 } 52 53 // Returns the bounds of |view| in widget coordinates. 54 gfx::Rect GetBoundsInWidget(views::View* view) { 55 return view->ConvertRectToWidget(view->GetLocalBounds()); 56 } 57 58 // Toggle the browser's fullscreen state. 59 void ToggleFullscreen() { 60 // NOTIFICATION_FULLSCREEN_CHANGED is sent asynchronously. The notification 61 // is used to trigger changes in whether the shelf is auto hidden and 62 // whether a "light bar" version of the tab strip is used when the 63 // top-of-window views are hidden. 64 scoped_ptr<FullscreenNotificationObserver> waiter( 65 new FullscreenNotificationObserver()); 66 chrome::ToggleFullscreenMode(browser()); 67 waiter->Wait(); 68 } 69 70 // Set whether the browser is in tab fullscreen. 71 void SetTabFullscreen(bool tab_fullscreen) { 72 content::WebContents* web_contents = 73 browser_view()->GetContentsWebViewForTest()->GetWebContents(); 74 scoped_ptr<FullscreenNotificationObserver> waiter( 75 new FullscreenNotificationObserver()); 76 browser()->fullscreen_controller()->ToggleFullscreenModeForTab( 77 web_contents, tab_fullscreen); 78 waiter->Wait(); 79 } 80 81 // Attempt revealing the top-of-window views. 82 void AttemptReveal() { 83 if (!revealed_lock_.get()) { 84 revealed_lock_.reset(controller_->GetRevealedLock( 85 ImmersiveModeControllerAsh::ANIMATE_REVEAL_NO)); 86 } 87 } 88 89 // Attempt unrevealing the top-of-window views. 90 void AttemptUnreveal() { 91 revealed_lock_.reset(); 92 } 93 94 ImmersiveModeController* controller() { return controller_; } 95 96 private: 97 // Not owned. 98 ImmersiveModeController* controller_; 99 100 scoped_ptr<ImmersiveRevealedLock> revealed_lock_; 101 102 DISALLOW_COPY_AND_ASSIGN(ImmersiveModeControllerAshTest); 103 }; 104 105 // Test the layout and visibility of the tabstrip, toolbar and TopContainerView 106 // in immersive fullscreen. 107 TEST_F(ImmersiveModeControllerAshTest, Layout) { 108 AddTab(browser(), GURL("about:blank")); 109 110 TabStrip* tabstrip = browser_view()->tabstrip(); 111 ToolbarView* toolbar = browser_view()->toolbar(); 112 views::WebView* contents_web_view = 113 browser_view()->GetContentsWebViewForTest(); 114 115 // Immersive fullscreen starts out disabled. 116 ASSERT_FALSE(browser_view()->GetWidget()->IsFullscreen()); 117 ASSERT_FALSE(controller()->IsEnabled()); 118 119 // By default, the tabstrip and toolbar should be visible. 120 EXPECT_TRUE(tabstrip->visible()); 121 EXPECT_TRUE(toolbar->visible()); 122 123 ToggleFullscreen(); 124 EXPECT_TRUE(browser_view()->GetWidget()->IsFullscreen()); 125 EXPECT_TRUE(controller()->IsEnabled()); 126 EXPECT_FALSE(controller()->IsRevealed()); 127 128 // Entering immersive fullscreen should make the tab strip use the immersive 129 // style and hide the toolbar. 130 EXPECT_TRUE(tabstrip->visible()); 131 EXPECT_TRUE(tabstrip->IsImmersiveStyle()); 132 EXPECT_FALSE(toolbar->visible()); 133 134 // The tab indicators should be flush with the top of the widget. 135 EXPECT_EQ(0, GetBoundsInWidget(tabstrip).y()); 136 137 // The web contents should be immediately below the tab indicators. 138 EXPECT_EQ(Tab::GetImmersiveHeight(), 139 GetBoundsInWidget(contents_web_view).y()); 140 141 // Revealing the top-of-window views should set the tab strip back to the 142 // normal style and show the toolbar. 143 AttemptReveal(); 144 EXPECT_TRUE(controller()->IsRevealed()); 145 EXPECT_TRUE(tabstrip->visible()); 146 EXPECT_FALSE(tabstrip->IsImmersiveStyle()); 147 EXPECT_TRUE(toolbar->visible()); 148 149 // The TopContainerView should be flush with the top edge of the widget. If 150 // it is not flush with the top edge the immersive reveal animation looks 151 // wonky. 152 EXPECT_EQ(0, GetBoundsInWidget(browser_view()->top_container()).y()); 153 154 // The web contents should be at the same y position as they were when the 155 // top-of-window views were hidden. 156 EXPECT_EQ(Tab::GetImmersiveHeight(), 157 GetBoundsInWidget(contents_web_view).y()); 158 159 // Repeat the test for when in both immersive fullscreen and tab fullscreen. 160 SetTabFullscreen(true); 161 // Hide and reveal the top-of-window views so that they get relain out. 162 AttemptUnreveal(); 163 AttemptReveal(); 164 165 // The tab strip and toolbar should still be visible and the TopContainerView 166 // should still be flush with the top edge of the widget. 167 EXPECT_TRUE(controller()->IsRevealed()); 168 EXPECT_TRUE(tabstrip->visible()); 169 EXPECT_FALSE(tabstrip->IsImmersiveStyle()); 170 EXPECT_TRUE(toolbar->visible()); 171 EXPECT_EQ(0, GetBoundsInWidget(browser_view()->top_container()).y()); 172 173 // The web contents should be flush with the top edge of the widget when in 174 // both immersive and tab fullscreen. 175 EXPECT_EQ(0, GetBoundsInWidget(contents_web_view).y()); 176 177 // Hide the top-of-window views. Both the tab strip and the toolbar should 178 // hide when in both immersive and tab fullscreen. 179 AttemptUnreveal(); 180 EXPECT_FALSE(controller()->IsRevealed()); 181 EXPECT_FALSE(tabstrip->visible()); 182 EXPECT_FALSE(toolbar->visible()); 183 184 // The web contents should still be flush with the edge of the widget. 185 EXPECT_EQ(0, GetBoundsInWidget(contents_web_view).y()); 186 187 // Exiting both immersive and tab fullscreen should show the tab strip and 188 // toolbar. 189 ToggleFullscreen(); 190 EXPECT_FALSE(browser_view()->GetWidget()->IsFullscreen()); 191 EXPECT_FALSE(controller()->IsEnabled()); 192 EXPECT_FALSE(controller()->IsRevealed()); 193 EXPECT_TRUE(tabstrip->visible()); 194 EXPECT_FALSE(tabstrip->IsImmersiveStyle()); 195 EXPECT_TRUE(toolbar->visible()); 196 } 197 198 // Test that the browser commands which are usually disabled in fullscreen are 199 // are enabled in immersive fullscreen. 200 TEST_F(ImmersiveModeControllerAshTest, EnabledCommands) { 201 ASSERT_FALSE(controller()->IsEnabled()); 202 EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_OPEN_CURRENT_URL)); 203 EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_ABOUT)); 204 EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_LOCATION)); 205 206 ToggleFullscreen(); 207 EXPECT_TRUE(controller()->IsEnabled()); 208 EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_OPEN_CURRENT_URL)); 209 EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_ABOUT)); 210 EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_LOCATION)); 211 } 212 213 // Test that restoring a window properly exits immersive fullscreen. 214 TEST_F(ImmersiveModeControllerAshTest, ExitUponRestore) { 215 ASSERT_FALSE(controller()->IsEnabled()); 216 ToggleFullscreen(); 217 AttemptReveal(); 218 ASSERT_TRUE(controller()->IsEnabled()); 219 ASSERT_TRUE(controller()->IsRevealed()); 220 ASSERT_TRUE(browser_view()->GetWidget()->IsFullscreen()); 221 222 browser_view()->GetWidget()->Restore(); 223 EXPECT_FALSE(controller()->IsEnabled()); 224 } 225 226 // Test how being simultaneously in tab fullscreen and immersive fullscreen 227 // affects the shelf visibility and whether the tab indicators are hidden. 228 TEST_F(ImmersiveModeControllerAshTest, TabAndBrowserFullscreen) { 229 AddTab(browser(), GURL("about:blank")); 230 231 // The shelf should start out as visible. 232 ash::ShelfLayoutManager* shelf = 233 ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager(); 234 ASSERT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state()); 235 236 // 1) Test that entering tab fullscreen from immersive fullscreen hides the 237 // tab indicators and the shelf. 238 ToggleFullscreen(); 239 ASSERT_TRUE(controller()->IsEnabled()); 240 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); 241 EXPECT_FALSE(controller()->ShouldHideTabIndicators()); 242 243 SetTabFullscreen(true); 244 ASSERT_TRUE(controller()->IsEnabled()); 245 EXPECT_EQ(ash::SHELF_HIDDEN, shelf->visibility_state()); 246 EXPECT_TRUE(controller()->ShouldHideTabIndicators()); 247 248 // 2) Test that exiting tab fullscreen shows the tab indicators and autohides 249 // the shelf. 250 SetTabFullscreen(false); 251 ASSERT_TRUE(controller()->IsEnabled()); 252 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); 253 EXPECT_FALSE(controller()->ShouldHideTabIndicators()); 254 255 // 3) Test that exiting tab fullscreen and immersive fullscreen 256 // simultaneously correctly updates the shelf visibility and whether the tab 257 // indicators should be hidden. 258 SetTabFullscreen(true); 259 ToggleFullscreen(); 260 ASSERT_FALSE(controller()->IsEnabled()); 261 EXPECT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state()); 262 EXPECT_TRUE(controller()->ShouldHideTabIndicators()); 263 } 264 265 class ImmersiveModeControllerAshTestHostedApp 266 : public ImmersiveModeControllerAshTest { 267 public: 268 ImmersiveModeControllerAshTestHostedApp() 269 : ImmersiveModeControllerAshTest(Browser::TYPE_POPUP, 270 chrome::HOST_DESKTOP_TYPE_ASH, 271 true) { 272 } 273 virtual ~ImmersiveModeControllerAshTestHostedApp() {} 274 275 private: 276 DISALLOW_COPY_AND_ASSIGN(ImmersiveModeControllerAshTestHostedApp); 277 }; 278 279 // Test the layout and visibility of the TopContainerView and web contents when 280 // a hosted app is put into immersive fullscreen. 281 TEST_F(ImmersiveModeControllerAshTestHostedApp, Layout) { 282 // Add a tab because the browser starts out without any tabs at all. 283 AddTab(browser(), GURL("about:blank")); 284 285 TabStrip* tabstrip = browser_view()->tabstrip(); 286 ToolbarView* toolbar = browser_view()->toolbar(); 287 views::WebView* contents_web_view = 288 browser_view()->GetContentsWebViewForTest(); 289 views::View* top_container = browser_view()->top_container(); 290 291 // Immersive fullscreen starts out disabled. 292 ASSERT_FALSE(browser_view()->GetWidget()->IsFullscreen()); 293 ASSERT_FALSE(controller()->IsEnabled()); 294 295 // The tabstrip and toolbar are not visible for hosted apps. 296 EXPECT_FALSE(tabstrip->visible()); 297 EXPECT_FALSE(toolbar->visible()); 298 299 // The window header should be above the web contents. 300 int header_height = GetBoundsInWidget(contents_web_view).y(); 301 302 ToggleFullscreen(); 303 EXPECT_TRUE(browser_view()->GetWidget()->IsFullscreen()); 304 EXPECT_TRUE(controller()->IsEnabled()); 305 EXPECT_FALSE(controller()->IsRevealed()); 306 307 // Entering immersive fullscreen should make the web contents flush with the 308 // top of the widget. 309 EXPECT_FALSE(tabstrip->visible()); 310 EXPECT_FALSE(toolbar->visible()); 311 EXPECT_TRUE(top_container->GetVisibleBounds().IsEmpty()); 312 EXPECT_EQ(0, GetBoundsInWidget(contents_web_view).y()); 313 314 // Reveal the window header. 315 AttemptReveal(); 316 317 // The tabstrip and toolbar should still be hidden and the web contents should 318 // still be flush with the top of the screen. 319 EXPECT_FALSE(tabstrip->visible()); 320 EXPECT_FALSE(toolbar->visible()); 321 EXPECT_EQ(0, GetBoundsInWidget(contents_web_view).y()); 322 323 // During an immersive reveal, the window header should be painted to the 324 // TopContainerView. The TopContainerView should be flush with the top of the 325 // widget and have |header_height|. 326 gfx::Rect top_container_bounds_in_widget(GetBoundsInWidget(top_container)); 327 EXPECT_EQ(0, top_container_bounds_in_widget.y()); 328 EXPECT_EQ(header_height, top_container_bounds_in_widget.height()); 329 330 // Exit immersive fullscreen. The web contents should be back below the window 331 // header. 332 ToggleFullscreen(); 333 EXPECT_FALSE(browser_view()->GetWidget()->IsFullscreen()); 334 EXPECT_FALSE(controller()->IsEnabled()); 335 EXPECT_FALSE(tabstrip->visible()); 336 EXPECT_FALSE(toolbar->visible()); 337 EXPECT_EQ(header_height, GetBoundsInWidget(contents_web_view).y()); 338 } 339