| // Copyright 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h" |
| |
| #include "ash/ash_switches.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/shelf/shelf_layout_manager.h" |
| #include "ash/shelf/shelf_types.h" |
| #include "ash/shell.h" |
| #include "ash/wm/window_properties.h" |
| #include "ash/wm/window_util.h" |
| #include "base/command_line.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/fullscreen/fullscreen_controller.h" |
| #include "chrome/browser/ui/fullscreen/fullscreen_controller_test.h" |
| #include "chrome/browser/ui/immersive_fullscreen_configuration.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "chrome/browser/ui/views/frame/top_container_view.h" |
| #include "chrome/browser/ui/views/tabs/tab.h" |
| #include "chrome/browser/ui/views/tabs/tab_strip.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_contents_view.h" |
| #include "content/public/test/test_utils.h" |
| #include "ui/aura/client/screen_position_client.h" |
| #include "ui/aura/env.h" |
| #include "ui/aura/root_window.h" |
| #include "ui/compositor/layer_animator.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/events/event.h" |
| #include "ui/gfx/rect.h" |
| #include "ui/views/view.h" |
| |
| using ui::ScopedAnimationDurationScaleMode; |
| |
| namespace { |
| |
| // Returns the bounds of |view| in widget coordinates. |
| gfx::Rect GetRectInWidget(views::View* view) { |
| return view->ConvertRectToWidget(view->GetLocalBounds()); |
| } |
| |
| } // namespace |
| |
| // TODO(jamescook): If immersive mode becomes popular on CrOS, consider porting |
| // it to other Aura platforms (win_aura, linux_aura). http://crbug.com/163931 |
| #if defined(OS_CHROMEOS) |
| |
| class ImmersiveModeControllerAshTest : public InProcessBrowserTest { |
| public: |
| ImmersiveModeControllerAshTest() : browser_view_(NULL), controller_(NULL) {} |
| virtual ~ImmersiveModeControllerAshTest() {} |
| |
| BrowserView* browser_view() { return browser_view_; } |
| ImmersiveModeControllerAsh* controller() { return controller_; } |
| |
| // content::BrowserTestBase overrides: |
| virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { |
| ImmersiveFullscreenConfiguration::EnableImmersiveFullscreenForTest(); |
| } |
| |
| virtual void SetUpOnMainThread() OVERRIDE { |
| ASSERT_TRUE(ImmersiveFullscreenConfiguration::UseImmersiveFullscreen()); |
| browser_view_ = static_cast<BrowserView*>(browser()->window()); |
| controller_ = static_cast<ImmersiveModeControllerAsh*>( |
| browser_view_->immersive_mode_controller()); |
| controller_->DisableAnimationsForTest(); |
| zero_duration_mode_.reset(new ScopedAnimationDurationScaleMode( |
| ScopedAnimationDurationScaleMode::ZERO_DURATION)); |
| } |
| |
| virtual void CleanUpOnMainThread() OVERRIDE { |
| zero_duration_mode_.reset(); |
| } |
| |
| private: |
| BrowserView* browser_view_; |
| ImmersiveModeControllerAsh* controller_; |
| scoped_ptr<ScopedAnimationDurationScaleMode> zero_duration_mode_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ImmersiveModeControllerAshTest); |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ImmersiveModeControllerAshTest, ImmersiveMode) { |
| views::View* contents_view = browser_view()->GetTabContentsContainerView(); |
| |
| // Immersive mode is not on by default. |
| EXPECT_FALSE(controller()->IsEnabled()); |
| EXPECT_FALSE(controller()->ShouldHideTopViews()); |
| |
| // Top-of-window views are visible. |
| EXPECT_TRUE(browser_view()->IsTabStripVisible()); |
| EXPECT_TRUE(browser_view()->IsToolbarVisible()); |
| |
| // Usual commands are enabled. |
| EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_OPEN_CURRENT_URL)); |
| EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_ABOUT)); |
| EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FULLSCREEN)); |
| |
| // Turning immersive mode on sets the toolbar to immersive style and hides |
| // the top-of-window views while leaving the tab strip visible. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_TRUE(browser_view()->IsFullscreen()); |
| EXPECT_TRUE(controller()->IsEnabled()); |
| EXPECT_TRUE(controller()->ShouldHideTopViews()); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| EXPECT_TRUE(browser_view()->tabstrip()->IsImmersiveStyle()); |
| EXPECT_TRUE(browser_view()->IsTabStripVisible()); |
| EXPECT_FALSE(browser_view()->IsToolbarVisible()); |
| // Content area is immediately below the tab indicators. |
| EXPECT_EQ(GetRectInWidget(browser_view()).y() + Tab::GetImmersiveHeight(), |
| GetRectInWidget(contents_view).y()); |
| |
| // Commands are still enabled (usually fullscreen disables these). |
| EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_OPEN_CURRENT_URL)); |
| EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_ABOUT)); |
| EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FULLSCREEN)); |
| |
| // Trigger a reveal keeps us in immersive mode, but top-of-window views |
| // become visible. |
| controller()->StartRevealForTest(true); |
| EXPECT_TRUE(controller()->IsEnabled()); |
| EXPECT_FALSE(controller()->ShouldHideTopViews()); |
| EXPECT_TRUE(controller()->IsRevealed()); |
| EXPECT_FALSE(browser_view()->tabstrip()->IsImmersiveStyle()); |
| EXPECT_TRUE(browser_view()->IsTabStripVisible()); |
| EXPECT_TRUE(browser_view()->IsToolbarVisible()); |
| // Shelf hide triggered by enabling immersive mode eventually changes the |
| // widget bounds and causes a Layout(). Force it to happen early for test. |
| browser_view()->parent()->Layout(); |
| // Content area is still immediately below the tab indicators. |
| EXPECT_EQ(GetRectInWidget(browser_view()).y() + Tab::GetImmersiveHeight(), |
| GetRectInWidget(contents_view).y()); |
| |
| // End reveal by moving the mouse off the top-of-window views. We |
| // should stay in immersive mode, but the toolbar should go invisible. |
| controller()->SetMouseHoveredForTest(false); |
| EXPECT_TRUE(controller()->IsEnabled()); |
| EXPECT_TRUE(controller()->ShouldHideTopViews()); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| EXPECT_TRUE(browser_view()->tabstrip()->IsImmersiveStyle()); |
| EXPECT_TRUE(browser_view()->IsTabStripVisible()); |
| EXPECT_FALSE(browser_view()->IsToolbarVisible()); |
| |
| // Disabling immersive mode puts us back to the beginning. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_FALSE(browser_view()->IsFullscreen()); |
| EXPECT_FALSE(controller()->IsEnabled()); |
| EXPECT_FALSE(controller()->ShouldHideTopViews()); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| EXPECT_FALSE(browser_view()->tabstrip()->IsImmersiveStyle()); |
| EXPECT_TRUE(browser_view()->IsTabStripVisible()); |
| EXPECT_TRUE(browser_view()->IsToolbarVisible()); |
| |
| // Disabling immersive mode while we are revealed should take us back to |
| // the beginning. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_TRUE(browser_view()->IsFullscreen()); |
| EXPECT_TRUE(controller()->IsEnabled()); |
| controller()->StartRevealForTest(true); |
| |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_FALSE(browser_view()->IsFullscreen()); |
| EXPECT_FALSE(controller()->IsEnabled()); |
| EXPECT_FALSE(controller()->ShouldHideTopViews()); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| EXPECT_FALSE(browser_view()->tabstrip()->IsImmersiveStyle()); |
| EXPECT_TRUE(browser_view()->IsTabStripVisible()); |
| EXPECT_TRUE(browser_view()->IsToolbarVisible()); |
| |
| // When hiding the tab indicators, content is at the top of the browser view |
| // both before and during reveal. |
| controller()->SetForceHideTabIndicatorsForTest(true); |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_TRUE(browser_view()->IsFullscreen()); |
| EXPECT_FALSE(browser_view()->IsTabStripVisible()); |
| EXPECT_EQ(GetRectInWidget(browser_view()).y(), |
| GetRectInWidget(contents_view).y()); |
| controller()->StartRevealForTest(true); |
| EXPECT_TRUE(browser_view()->IsTabStripVisible()); |
| // Shelf hide triggered by enabling immersive mode eventually changes the |
| // widget bounds and causes a Layout(). Force it to happen early for test. |
| browser_view()->parent()->Layout(); |
| EXPECT_EQ(GetRectInWidget(browser_view()).y(), |
| GetRectInWidget(contents_view).y()); |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_FALSE(browser_view()->IsFullscreen()); |
| controller()->SetForceHideTabIndicatorsForTest(false); |
| |
| // Reveal ends when the mouse moves out of the reveal view. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_TRUE(browser_view()->IsFullscreen()); |
| EXPECT_TRUE(controller()->IsEnabled()); |
| controller()->StartRevealForTest(true); |
| controller()->SetMouseHoveredForTest(false); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| |
| // Restoring the window exits immersive mode. |
| browser_view()->GetWidget()->Restore(); |
| // Exiting immersive fullscreen occurs as a result of a task posted to the |
| // message loop. |
| content::RunAllPendingInMessageLoop(); |
| ASSERT_FALSE(browser_view()->IsFullscreen()); |
| EXPECT_FALSE(controller()->IsEnabled()); |
| EXPECT_FALSE(controller()->ShouldHideTopViews()); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| EXPECT_FALSE(browser_view()->tabstrip()->IsImmersiveStyle()); |
| EXPECT_TRUE(browser_view()->IsTabStripVisible()); |
| EXPECT_TRUE(browser_view()->IsToolbarVisible()); |
| |
| // Don't crash if we exit the test during a reveal. |
| if (!browser_view()->IsFullscreen()) |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_TRUE(browser_view()->IsFullscreen()); |
| ASSERT_TRUE(controller()->IsEnabled()); |
| controller()->StartRevealForTest(true); |
| } |
| |
| // Test behavior when the mouse becomes hovered without moving. |
| IN_PROC_BROWSER_TEST_F(ImmersiveModeControllerAshTest, |
| MouseHoveredWithoutMoving) { |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_TRUE(controller()->IsEnabled()); |
| |
| scoped_ptr<ImmersiveRevealedLock> lock; |
| |
| // 1) Test that if the mouse becomes hovered without the mouse moving due to a |
| // lock causing the top-of-window views to be revealed (and the mouse |
| // happening to be near the top of the screen), the top-of-window views do not |
| // hide till the mouse moves off of the top-of-window views. |
| controller()->SetMouseHoveredForTest(true); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| lock.reset(controller()->GetRevealedLock( |
| ImmersiveModeController::ANIMATE_REVEAL_NO)); |
| EXPECT_TRUE(controller()->IsRevealed()); |
| lock.reset(); |
| EXPECT_TRUE(controller()->IsRevealed()); |
| controller()->SetMouseHoveredForTest(false); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| |
| // 2) Test that if the mouse becomes hovered without moving because of a |
| // reveal in ImmersiveModeController::SetEnabled(true) and there are no locks |
| // keeping the top-of-window views revealed, that mouse hover does not prevent |
| // the top-of-window views from closing. |
| chrome::ToggleFullscreenMode(browser()); |
| controller()->SetMouseHoveredForTest(true); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| chrome::ToggleFullscreenMode(browser()); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| |
| // 3) Test that if the mouse becomes hovered without moving because of a |
| // reveal in ImmersiveModeController::SetEnabled(true) and there is a lock |
| // keeping the top-of-window views revealed, that the top-of-window views do |
| // not hide till the mouse moves off of the top-of-window views. |
| chrome::ToggleFullscreenMode(browser()); |
| controller()->SetMouseHoveredForTest(true); |
| lock.reset(controller()->GetRevealedLock( |
| ImmersiveModeController::ANIMATE_REVEAL_NO)); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| chrome::ToggleFullscreenMode(browser()); |
| EXPECT_TRUE(controller()->IsRevealed()); |
| lock.reset(); |
| EXPECT_TRUE(controller()->IsRevealed()); |
| controller()->SetMouseHoveredForTest(false); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| } |
| |
| // GetRevealedLock() specific tests. |
| IN_PROC_BROWSER_TEST_F(ImmersiveModeControllerAshTest, RevealedLock) { |
| scoped_ptr<ImmersiveRevealedLock> lock1; |
| scoped_ptr<ImmersiveRevealedLock> lock2; |
| |
| // Immersive mode is not on by default. |
| EXPECT_FALSE(controller()->IsEnabled()); |
| |
| // Move the mouse out of the way. |
| controller()->SetMouseHoveredForTest(false); |
| |
| // 1) Test acquiring and releasing a revealed state lock while immersive mode |
| // is disabled. Acquiring or releasing the lock should have no effect till |
| // immersive mode is enabled. |
| lock1.reset(controller()->GetRevealedLock( |
| ImmersiveModeController::ANIMATE_REVEAL_NO)); |
| EXPECT_FALSE(controller()->IsEnabled()); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| EXPECT_FALSE(controller()->ShouldHideTopViews()); |
| |
| // Immersive mode should start in the revealed state due to the lock. |
| chrome::ToggleFullscreenMode(browser()); |
| EXPECT_TRUE(controller()->IsEnabled()); |
| EXPECT_TRUE(controller()->IsRevealed()); |
| EXPECT_FALSE(controller()->ShouldHideTopViews()); |
| |
| chrome::ToggleFullscreenMode(browser()); |
| EXPECT_FALSE(controller()->IsEnabled()); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| EXPECT_FALSE(controller()->ShouldHideTopViews()); |
| |
| lock1.reset(); |
| EXPECT_FALSE(controller()->IsEnabled()); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| EXPECT_FALSE(controller()->ShouldHideTopViews()); |
| |
| // Immersive mode should start in the closed state because the lock is no |
| // longer held. |
| chrome::ToggleFullscreenMode(browser()); |
| EXPECT_TRUE(controller()->IsEnabled()); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| EXPECT_TRUE(controller()->ShouldHideTopViews()); |
| |
| // 2) Test that acquiring a revealed state lock reveals the top-of-window |
| // views if they are hidden. |
| EXPECT_FALSE(controller()->IsRevealed()); |
| lock1.reset(controller()->GetRevealedLock( |
| ImmersiveModeController::ANIMATE_REVEAL_NO)); |
| EXPECT_TRUE(controller()->IsRevealed()); |
| |
| // 3) Test that the top-of-window views are only hidden when all of the locks |
| // are released. |
| lock2.reset(controller()->GetRevealedLock( |
| ImmersiveModeController::ANIMATE_REVEAL_NO)); |
| lock1.reset(); |
| EXPECT_TRUE(controller()->IsRevealed()); |
| |
| lock2.reset(); |
| EXPECT_FALSE(controller()->IsRevealed()); |
| } |
| |
| // Shelf-specific immersive mode tests. |
| IN_PROC_BROWSER_TEST_F(ImmersiveModeControllerAshTest, ImmersiveShelf) { |
| // Shelf is visible when the test starts. |
| ash::internal::ShelfLayoutManager* shelf = |
| ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager(); |
| ASSERT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state()); |
| |
| // Turning immersive mode on sets the shelf to auto-hide. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_TRUE(browser_view()->IsFullscreen()); |
| ASSERT_TRUE(controller()->IsEnabled()); |
| EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); |
| |
| // Disabling immersive mode puts it back. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_FALSE(browser_view()->IsFullscreen()); |
| ASSERT_FALSE(controller()->IsEnabled()); |
| EXPECT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state()); |
| |
| // The user could toggle the launcher behavior. |
| shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); |
| EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); |
| |
| // Enabling immersive mode keeps auto-hide. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_TRUE(browser_view()->IsFullscreen()); |
| ASSERT_TRUE(controller()->IsEnabled()); |
| EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); |
| |
| // Disabling immersive mode maintains the user's auto-hide selection. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_FALSE(browser_view()->IsFullscreen()); |
| ASSERT_FALSE(controller()->IsEnabled()); |
| EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); |
| } |
| |
| // Test how being simultaneously in tab fullscreen and immersive fullscreen |
| // affects the shelf visibility and whether the tab indicators are hidden. |
| IN_PROC_BROWSER_TEST_F(ImmersiveModeControllerAshTest, |
| TabAndBrowserFullscreen) { |
| ash::internal::ShelfLayoutManager* shelf = |
| ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager(); |
| |
| controller()->SetForceHideTabIndicatorsForTest(false); |
| |
| // The shelf should start out as visible. |
| ASSERT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state()); |
| |
| // 1) Test that entering tab fullscreen from immersive mode hides the tab |
| // indicators and the shelf. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_TRUE(controller()->IsEnabled()); |
| EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); |
| EXPECT_FALSE(controller()->ShouldHideTabIndicators()); |
| |
| // The shelf visibility and the tab indicator visibility are updated as a |
| // result of NOTIFICATION_FULLSCREEN_CHANGED which is asynchronous. Wait for |
| // the notification before testing visibility. |
| scoped_ptr<FullscreenNotificationObserver> waiter( |
| new FullscreenNotificationObserver()); |
| |
| browser()->fullscreen_controller()->ToggleFullscreenModeForTab( |
| browser_view()->GetActiveWebContents(), true); |
| waiter->Wait(); |
| ASSERT_TRUE(controller()->IsEnabled()); |
| EXPECT_EQ(ash::SHELF_HIDDEN, shelf->visibility_state()); |
| EXPECT_TRUE(controller()->ShouldHideTabIndicators()); |
| |
| // 2) Test that exiting tab fullscreen shows the tab indicators and autohides |
| // the shelf. |
| waiter.reset(new FullscreenNotificationObserver()); |
| browser()->fullscreen_controller()->ToggleFullscreenModeForTab( |
| browser_view()->GetActiveWebContents(), false); |
| waiter->Wait(); |
| ASSERT_TRUE(controller()->IsEnabled()); |
| EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state()); |
| EXPECT_FALSE(controller()->ShouldHideTabIndicators()); |
| |
| // 3) Test that exiting tab fullscreen and immersive fullscreen |
| // simultaneously correctly updates the shelf visibility and whether the tab |
| // indicators should be hidden. |
| waiter.reset(new FullscreenNotificationObserver()); |
| browser()->fullscreen_controller()->ToggleFullscreenModeForTab( |
| browser_view()->GetActiveWebContents(), true); |
| waiter->Wait(); |
| waiter.reset(new FullscreenNotificationObserver()); |
| chrome::ToggleFullscreenMode(browser()); |
| waiter->Wait(); |
| |
| ASSERT_FALSE(controller()->IsEnabled()); |
| EXPECT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state()); |
| EXPECT_TRUE(controller()->ShouldHideTabIndicators()); |
| } |
| |
| // Validate top container touch insets are being updated at the correct time in |
| // immersive mode. |
| IN_PROC_BROWSER_TEST_F(ImmersiveModeControllerAshTest, |
| ImmersiveTopContainerInsets) { |
| content::WebContents* contents = browser_view()->GetActiveWebContents(); |
| aura::Window* window = contents->GetView()->GetContentNativeView(); |
| |
| // Turning immersive mode on sets positive top touch insets on the render view |
| // window. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_TRUE(browser_view()->IsFullscreen()); |
| ASSERT_TRUE(controller()->IsEnabled()); |
| EXPECT_TRUE(window->hit_test_bounds_override_outer_touch().top() > 0); |
| |
| // Trigger a reveal resets insets as now the touch target for the top |
| // container is large enough. |
| controller()->StartRevealForTest(true); |
| EXPECT_TRUE(window->hit_test_bounds_override_outer_touch().top() == 0); |
| |
| // End reveal by moving the mouse off the top-of-window views. We |
| // should see the top insets being positive again to allow a bigger touch |
| // target. |
| controller()->SetMouseHoveredForTest(false); |
| EXPECT_TRUE(window->hit_test_bounds_override_outer_touch().top() > 0); |
| |
| // Disabling immersive mode resets the top touch insets to 0. |
| chrome::ToggleFullscreenMode(browser()); |
| ASSERT_FALSE(browser_view()->IsFullscreen()); |
| ASSERT_FALSE(controller()->IsEnabled()); |
| EXPECT_TRUE(window->hit_test_bounds_override_outer_touch().top() == 0); |
| } |
| |
| #endif // defined(OS_CHROMEOS) |