// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
#include "chrome/browser/ui/focus/browser_focus_controller.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
#include "chrome/browser/ui/tabs/split_tab_metrics.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/test/base/chrome_test_path_utils.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/input/native_web_keyboard_event.h"
#include "components/split_tabs/split_tab_visual_data.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_navigation_observer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ozone_buildflags.h"
#include "ui/base/ui_base_features.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/bubble/bubble_dialog_model_host.h"
#include "ui/views/buildflags.h"
#include "ui/views/test/ax_event_counter.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/test/widget_activation_waiter.h"

#if BUILDFLAG(IS_MAC)
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_commands_mac.h"
#include "chrome/test/base/interactive_test_utils.h"
#endif

#if BUILDFLAG(IS_CHROMEOS)
#include "ash/wm/window_pin_util.h"
#include "chrome/browser/ash/boca/on_task/on_task_locked_controller.h"
#endif

using views::FocusManager;

namespace {

class BrowserViewTest : public InProcessBrowserTest {
 public:
  BrowserViewTest() : ax_observer_(views::AXUpdateNotifier::Get()) {}
  ~BrowserViewTest() override = default;
  BrowserViewTest(const BrowserViewTest&) = delete;
  BrowserViewTest& operator=(const BrowserViewTest&) = delete;

  BrowserView* browser_view() {
    return BrowserView::GetBrowserViewForBrowser(browser());
  }

  void SetUpOnMainThread() override {
#if BUILDFLAG(IS_MAC)
    chrome::SetAlwaysShowToolbarInFullscreenForTesting(browser(), true);
    // Ensure that the browser window is activated. BrowserView::Show calls
    // into BridgedNativeWidgetImpl::SetVisibilityState and makeKeyAndOrderFront
    // there somehow does not change the window's key status on bot.
    ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
#endif
  }

  void TearDownOnMainThread() override {
#if BUILDFLAG(IS_MAC)
    chrome::SetAlwaysShowToolbarInFullscreenForTesting(browser(), true);
#endif
  }

 protected:
  views::test::AXEventCounter ax_observer_;
};

}  // namespace

IN_PROC_BROWSER_TEST_F(BrowserViewTest, FullscreenClearsFocus) {
  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
  LocationBarView* location_bar_view = browser_view->GetLocationBarView();
  FocusManager* focus_manager = browser_view->GetFocusManager();

  // Focus starts in the location bar or one of its children.
  EXPECT_TRUE(location_bar_view->Contains(focus_manager->GetFocusedView()));

  // Enter into fullscreen mode.
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  EXPECT_TRUE(browser_view->IsFullscreen());

  // Focus is released from the location bar.
  EXPECT_FALSE(location_bar_view->Contains(focus_manager->GetFocusedView()));
}

// Test that the view tree order is preserved after entering and exiting
// immersive fullscreen. Immersive fullscreen is only supported on ChromeOS and
// MacOS at this point in time.
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(BrowserViewTest, ImmersiveFullscreenViewTreeOrder) {
  auto* const immersive_mode_controller =
      ImmersiveModeController::From(browser());
  ASSERT_TRUE(immersive_mode_controller);

  BrowserView* view = browser_view();
  std::vector<views::View*> children_before;
  for (const auto& child : view->children()) {
    children_before.push_back(child);
  }

  // Enter immersive fullscreen.
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  EXPECT_TRUE(immersive_mode_controller->IsEnabled());

  // Exit immersive fullscreen.
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  EXPECT_FALSE(immersive_mode_controller->IsEnabled());

  std::vector<views::View*> children_after;
  for (const auto& child : view->children()) {
    children_after.push_back(child);
  }

  EXPECT_EQ(children_before, children_after);
}
#endif

// Test that holding Esc correctly exits fullscreen when focus is on the native
// browser UI.
IN_PROC_BROWSER_TEST_F(BrowserViewTest,
                       PressAndHoldEscExitsFullscreenWithNativeFocus) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  ASSERT_TRUE(browser_view()->IsFullscreen());
  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));

  // When fullscreening a window that is displaying the NTP, focus can remain on
  // the native window container (the RootView) rather than the
  // RenderWidgetHostView. This prevents the 'Esc' keypress from being handled
  // by the WebContents delegate and instead causes it to be routed through the
  // FocusManager's accelerator system (as IDC_CLOSE_FIND_OR_STOP). We use
  // ProcessAccelerator here to specifically verify that our fix in the native
  // accelerator path correctly catches this event and triggers the
  // ExclusiveAccessManager.
  ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, ui::EF_NONE);
  browser_view()->GetFocusManager()->ProcessAccelerator(escape_accelerator);

  // Hold for kHoldEscapeTime plus a buffer to ensure the timer fires, and then
  // release.
  base::RunLoop run_loop;
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(2000));
  run_loop.Run();
  ui::Accelerator escape_released(ui::VKEY_ESCAPE, ui::EF_NONE);
  escape_released.set_key_state(ui::Accelerator::KeyState::RELEASED);
  browser_view()->GetFocusManager()->ProcessAccelerator(escape_released);

  ui_test_utils::FullscreenWaiter(
      browser(), ui_test_utils::FullscreenWaiter::kNoFullscreen)
      .Wait();

  EXPECT_FALSE(browser_view()->IsFullscreen());
}

// Test that a quick tap of Esc does NOT exit fullscreen when focus is on the
// native browser UI. This verifies that the release accelerator correctly
// stops the fullscreen exit timer.
IN_PROC_BROWSER_TEST_F(BrowserViewTest,
                       QuickTapEscDoesNotExitFullscreenWithNativeFocus) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  ASSERT_TRUE(browser_view()->IsFullscreen());
  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));

  // Simulate a quick press and release of 'Esc'.
  ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, ui::EF_NONE);
  browser_view()->GetFocusManager()->ProcessAccelerator(escape_accelerator);

  ui::Accelerator escape_released(ui::VKEY_ESCAPE, ui::EF_NONE);
  escape_released.set_key_state(ui::Accelerator::KeyState::RELEASED);
  browser_view()->GetFocusManager()->ProcessAccelerator(escape_released);

  // Wait for longer than kHoldEscapeTime (1500ms) to ensure it doesn't exit.
  base::RunLoop run_loop;
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(2000));
  run_loop.Run();

  EXPECT_TRUE(browser_view()->IsFullscreen());
}

// Test whether the top view including toolbar and tab strip shows up or hides
// correctly in browser fullscreen mode.
#if BUILDFLAG(IS_MAC)
// TODO(crbug.com/523216992): Re-enable this test.
#define MAYBE_BrowserFullscreenShowTopView DISABLED_BrowserFullscreenShowTopView
#else
#define MAYBE_BrowserFullscreenShowTopView BrowserFullscreenShowTopView
#endif
IN_PROC_BROWSER_TEST_F(BrowserViewTest, MAYBE_BrowserFullscreenShowTopView) {
  BrowserView* const browser_view =
      BrowserView::GetBrowserViewForBrowser(browser());

  // The top view should always show up in regular mode.
  EXPECT_FALSE(browser_view->IsFullscreen());
  EXPECT_TRUE(browser_view->GetTabStripVisible());

  // Enter into fullscreen mode.
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  EXPECT_TRUE(browser_view->IsFullscreen());

  bool top_view_in_browser_fullscreen = false;
#if BUILDFLAG(IS_MAC)
  // The top view should show up by default.
  EXPECT_TRUE(browser_view->GetTabStripVisible());
  // The 'Always Show Bookmarks Bar' should be enabled.
  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_SHOW_BOOKMARK_BAR));

  // Return back to normal mode and toggle to not show the top view in full
  // screen mode.
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  EXPECT_FALSE(browser_view->IsFullscreen());
  // Disable 'Always Show Toolbar in Full Screen'.
  chrome::SetAlwaysShowToolbarInFullscreenForTesting(browser(), false);

  // While back to fullscreen mode, the top view no longer shows up.
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  EXPECT_TRUE(browser_view->IsFullscreen());
  EXPECT_FALSE(browser_view->GetTabStripVisible());
  // In non-immersive mode, the bookmark visibility cannot be changed because
  // the toolbar is invisible. In immersive mode, the bookmark visibility should
  // be able to change because the toolbar cannot be permanently hidden.
  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_SHOW_BOOKMARK_BAR));

  if (ImmersiveModeController::From(browser())->IsEnabled()) {
    // Move mouse to the upper border of the browser window and the toolbar
    // should become visible.
    ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
        browser_view->GetBoundsInScreen().top_center()));
    views::test::PropertyWaiter(
        base::BindRepeating(&BrowserView::GetTabStripVisible,
                            base::Unretained(browser_view)),
        true)
        .Wait();
    EXPECT_TRUE(browser_view->GetTabStripVisible());
  }

  // Test toggling toolbar while being in fullscreen mode.
  chrome::SetAlwaysShowToolbarInFullscreenForTesting(browser(), true);
  EXPECT_TRUE(browser_view->IsFullscreen());
  top_view_in_browser_fullscreen = true;
#else
  // In immersive fullscreen mode, the top view should show up; otherwise, it
  // always hides.
  if (ImmersiveModeController::From(browser())->IsEnabled()) {
    top_view_in_browser_fullscreen = true;
  }
#endif
  EXPECT_EQ(top_view_in_browser_fullscreen, browser_view->GetTabStripVisible());
  // The 'Always Show Bookmarks Bar' should be enabled if top view is shown.
  EXPECT_EQ(top_view_in_browser_fullscreen,
            chrome::IsCommandEnabled(browser(), IDC_SHOW_BOOKMARK_BAR));

  // Enter into tab fullscreen mode from browser fullscreen mode.
  FullscreenController* controller = browser()
                                         ->GetFeatures()
                                         .exclusive_access_manager()
                                         ->fullscreen_controller();
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  controller->EnterFullscreenModeForTab(web_contents->GetPrimaryMainFrame());
  EXPECT_TRUE(browser_view->IsFullscreen());
  bool top_view_in_tab_fullscreen =
      ImmersiveModeController::From(browser())->IsEnabled();
  EXPECT_EQ(top_view_in_tab_fullscreen, browser_view->GetTabStripVisible());
  // The 'Always Show Bookmarks Bar' should be disabled in tab fullscreen mode.
  EXPECT_EQ(top_view_in_tab_fullscreen,
            chrome::IsCommandEnabled(browser(), IDC_SHOW_BOOKMARK_BAR));

  // Return back to browser fullscreen mode.
  input::NativeWebKeyboardEvent event(
      blink::WebInputEvent::Type::kKeyDown, blink::WebInputEvent::kNoModifiers,
      blink::WebInputEvent::GetStaticTimeStampForTests());
  event.windows_key_code = ui::VKEY_ESCAPE;
  browser()->GetFeatures().exclusive_access_manager()->HandleUserKeyEvent(
      event);
  EXPECT_TRUE(browser_view->IsFullscreen());
  EXPECT_EQ(top_view_in_browser_fullscreen, browser_view->GetTabStripVisible());
  // This makes sure that the layout was updated accordingly.
  EXPECT_EQ(top_view_in_browser_fullscreen,
            browser_view->horizontal_tab_strip_for_testing()->GetVisible());
  EXPECT_EQ(top_view_in_browser_fullscreen,
            chrome::IsCommandEnabled(browser(), IDC_SHOW_BOOKMARK_BAR));

  // Return to regular mode.
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  EXPECT_FALSE(browser_view->IsFullscreen());
  EXPECT_TRUE(browser_view->GetTabStripVisible());
}

// Test whether the top view including toolbar and tab strip appears or hides
// correctly in tab fullscreen mode.
IN_PROC_BROWSER_TEST_F(BrowserViewTest, TabFullscreenShowTopView) {
  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());

  // The top view should always show up in regular mode.
  EXPECT_FALSE(browser_view->IsFullscreen());
  EXPECT_TRUE(browser_view->GetTabStripVisible());

  // Enter into tab fullscreen mode.
  FullscreenController* controller = browser()
                                         ->GetFeatures()
                                         .exclusive_access_manager()
                                         ->fullscreen_controller();
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  controller->EnterFullscreenModeForTab(web_contents->GetPrimaryMainFrame());
  EXPECT_TRUE(browser_view->IsFullscreen());

  // The top view should not show up.
  EXPECT_TRUE(views::test::PropertyWaiter(
                  base::BindRepeating(&BrowserView::GetTabStripVisible,
                                      base::Unretained(browser_view)),
                  false)
                  .Wait());

  // After exiting the fullscreen mode, the top view should show up again.
  controller->ExitFullscreenModeForTab(web_contents);
  EXPECT_FALSE(browser_view->IsFullscreen());
  EXPECT_TRUE(browser_view->GetTabStripVisible());
}

// Test whether the split view is hidden when a tab is fullscreened.
IN_PROC_BROWSER_TEST_F(BrowserViewTest, TabFullscreenHideSplitView) {
  // Add a second tab and create a split
  chrome::AddTabAt(browser(), GURL(), -1, true);
  browser()->GetTabStripModel()->ActivateTabAt(0);
  browser()->GetTabStripModel()->AddToNewSplit(
      {1}, split_tabs::SplitTabVisualData(),
      split_tabs::SplitTabCreatedSource::kToolbarButton);

  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());

  // Split view should be open
  EXPECT_FALSE(browser_view->IsFullscreen());
  EXPECT_TRUE(browser_view->IsInSplitView());

  // Enter into tab fullscreen mode.
  FullscreenController* controller = browser()
                                         ->GetFeatures()
                                         .exclusive_access_manager()
                                         ->fullscreen_controller();
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  controller->EnterFullscreenModeForTab(web_contents->GetPrimaryMainFrame());
  EXPECT_TRUE(browser_view->IsFullscreen());

  // The split view should be closed.
  EXPECT_FALSE(browser_view->IsInSplitView());
  EXPECT_TRUE(views::test::PropertyWaiter(
                  base::BindRepeating(&BrowserView::GetTabStripVisible,
                                      base::Unretained(browser_view)),
                  false)
                  .Wait());

  // After exiting the fullscreen mode, the top view should show up again.
  controller->ExitFullscreenModeForTab(web_contents);
  EXPECT_FALSE(browser_view->IsFullscreen());
  EXPECT_TRUE(browser_view->IsInSplitView());
}

// Test whether bookmark bar shows up or hides correctly for fullscreen modes.
IN_PROC_BROWSER_TEST_F(BrowserViewTest, FullscreenShowBookmarkBar) {
  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());

  // If the bookmark bar is not showing, enable showing it so that we can check
  // its state.
  if (!browser_view->IsBookmarkBarVisible()) {
    chrome::ToggleBookmarkBar(browser());
  }
#if BUILDFLAG(IS_MAC)
  // Disable showing toolbar in fullscreen mode to make its behavior similar to
  // other platforms.
  chrome::SetAlwaysShowToolbarInFullscreenForTesting(browser(), false);
#endif
  ASSERT_TRUE(AddTabAtIndex(0, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED));

  // Now the bookmark bar should show up in regular mode.
  EXPECT_FALSE(browser_view->IsFullscreen());
  EXPECT_TRUE(browser_view->IsBookmarkBarVisible());

  // Enter into fullscreen mode.
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  EXPECT_TRUE(browser_view->IsFullscreen());

  // Move to the center of the window so that the toolbar becomes hidden in
  // immersive mode.
  ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
      browser_view->GetBoundsInScreen().CenterPoint()));
  views::test::PropertyWaiter(
      base::BindRepeating(&BrowserView::IsBookmarkBarVisible,
                          base::Unretained(browser_view)),
      false)
      .Wait();
  EXPECT_FALSE(browser_view->GetTabStripVisible());

#if BUILDFLAG(IS_CHROMEOS)
  // Bookmark bar in immersive fullscreen mode on ChromeOS is accessible and
  // should be considered visible.
  EXPECT_TRUE(browser_view->IsBookmarkBarVisible());
#else
  EXPECT_FALSE(browser_view->IsBookmarkBarVisible());
#endif

#if BUILDFLAG(IS_MAC)
  // Test toggling toolbar state in fullscreen mode would also affect bookmark
  // bar state.
  chrome::SetAlwaysShowToolbarInFullscreenForTesting(browser(), true);
  EXPECT_TRUE(browser_view->GetTabStripVisible());
  EXPECT_TRUE(browser_view->IsBookmarkBarVisible());

  chrome::SetAlwaysShowToolbarInFullscreenForTesting(browser(), false);
  EXPECT_FALSE(browser_view->GetTabStripVisible());
  EXPECT_FALSE(browser_view->IsBookmarkBarVisible());
#endif

  // Exit from fullscreen mode.
  ui_test_utils::ToggleFullscreenModeAndWait(browser());
  EXPECT_FALSE(browser_view->IsFullscreen());
  EXPECT_TRUE(browser_view->GetTabStripVisible());
  EXPECT_TRUE(browser_view->IsBookmarkBarVisible());
}

// TODO(crbug.com/40598906): Only Aura platforms use the WindowActivated
// accessibility event. We need to harmonize the firing of accessibility events
// between platforms.
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
IN_PROC_BROWSER_TEST_F(BrowserViewTest, WindowActivatedAccessibleEvent) {
  // Wait for window activated event from the first browser window.
  // This event is asynchronous, it is emitted as a response to a system window
  // event. It is possible that we haven't received it yet when we run this test
  // and we need to explicitly wait for it.
  if (ax_observer_.GetCount(ax::mojom::Event::kWindowActivated) == 0) {
    ax_observer_.WaitForEvent(ax::mojom::Event::kWindowActivated);
  }
  ASSERT_EQ(1, ax_observer_.GetCount(ax::mojom::Event::kWindowActivated));

  // Create a new browser window and wait for event again.
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), GURL(url::kAboutBlankURL), WindowOpenDisposition::NEW_WINDOW,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
  if (ax_observer_.GetCount(ax::mojom::Event::kWindowActivated) == 1) {
    ax_observer_.WaitForEvent(ax::mojom::Event::kWindowActivated);
  }
  ASSERT_EQ(2, ax_observer_.GetCount(ax::mojom::Event::kWindowActivated));
}
#endif

#if BUILDFLAG(IS_MAC)
// TODO(crbug.com/40568702) NativeWidgetMac::Deactivate is not implemented on
// Mac.
#define MAYBE_FocusInactivePopupForAccessibility \
  DISABLED_FocusInactivePopupForAccessibility
#else
#define MAYBE_FocusInactivePopupForAccessibility \
  FocusInactivePopupForAccessibility
#endif
IN_PROC_BROWSER_TEST_F(BrowserViewTest,
                       MAYBE_FocusInactivePopupForAccessibility) {
  std::unique_ptr<ui::DialogModel> dialog_model =
      ui::DialogModel::Builder()
          .SetTitle(u"test")
          .SetIsAlertDialog()
          .AddOkButton(base::DoNothing())
          .Build();
  views::View* anchor = browser_view()->GetLocationBarView();
  auto bubble = std::make_unique<views::BubbleDialogModelHost>(
      std::move(dialog_model), anchor, views::BubbleBorder::TOP_RIGHT);
  bubble->set_close_on_deactivate(false);
  views::Widget* widget = views::BubbleDialogDelegate::CreateBubbleDeprecated(
      std::move(bubble), views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET);

  widget->Show();
  views::test::WaitForWidgetActive(widget, true);

  widget->Deactivate();
  widget->ShowInactive();
  EXPECT_TRUE(widget->IsVisible());
  EXPECT_FALSE(widget->IsActive());

  BrowserFocusController::From(browser_view()->browser())
      ->FocusInactivePopupForAccessibility();
  views::test::WaitForWidgetActive(widget, true);

  // Ensure the bubble's widget refreshed appropriately.
  EXPECT_TRUE(widget->IsVisible());
  EXPECT_TRUE(widget->IsActive());
}

class BrowserViewFullscreenTest : public BrowserViewTest {
 public:
  BrowserViewFullscreenTest() {
    feature_list_.InitAndEnableFeature(features::kAsyncFullscreenWindowState);
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

// Disabled on platforms where async fullscreen state transition is not
// yet supported.
// TODO(b/40276379): Apply this to all remaining desktop platforms.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_Fullscreen Fullscreen
#else
#define MAYBE_Fullscreen DISABLED_Fullscreen
#endif
IN_PROC_BROWSER_TEST_F(BrowserViewFullscreenTest, MAYBE_Fullscreen) {
#if BUILDFLAG(IS_LINUX)
  // Skip non wayland cases, now, such as X11, where the fullscreen
  // call async support is not yet completed.
  if (ui::OzonePlatform::GetPlatformNameForTest() != "wayland") {
    GTEST_SKIP();
  }
#endif

  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());

  // The top view should always show up in regular mode.
  EXPECT_FALSE(browser_view->IsFullscreen());

  // Enter into fullscreen mode.
  {
    ui_test_utils::FullscreenWaiter waiter(browser(),
                                           {.browser_fullscreen = true});
    chrome::ToggleFullscreenMode(browser());
    // The state won't change immediately.
    EXPECT_FALSE(browser_view->IsFullscreen());
    waiter.Wait();
    EXPECT_TRUE(browser_view->IsFullscreen());
  }

  // Exit from fullscreen mode.
  {
    ui_test_utils::FullscreenWaiter waiter(browser(),
                                           {.browser_fullscreen = false});
    chrome::ToggleFullscreenMode(browser());
    // The state won't change immediately.
    EXPECT_TRUE(browser_view->IsFullscreen());
    waiter.Wait();
    EXPECT_FALSE(browser_view->IsFullscreen());
  }
}

class BrowserViewLoadingAnimationTest
    : public BrowserViewTest,
      public testing::WithParamInterface<bool> {
 public:
  BrowserViewLoadingAnimationTest() {
    if (GetParam()) {
      feature_list_.InitAndEnableFeature(features::kCompositorLoadingThrobber);
    } else {
      feature_list_.InitAndDisableFeature(features::kCompositorLoadingThrobber);
    }
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

INSTANTIATE_TEST_SUITE_P(CompositorDrivenThrobber,
                         BrowserViewLoadingAnimationTest,
                         testing::Bool());

// TODO(b/342017720): Re-enable on Mac
#if BUILDFLAG(IS_MAC)
#define MAYBE_LoadingAnimationChangeOnMinimizeAndRestore \
  DISABLED_LoadingAnimationChangeOnMinimizeAndRestore
#else
#define MAYBE_LoadingAnimationChangeOnMinimizeAndRestore \
  LoadingAnimationChangeOnMinimizeAndRestore
#endif  // BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_P(BrowserViewLoadingAnimationTest,
                       MAYBE_LoadingAnimationChangeOnMinimizeAndRestore) {
  auto* contents = browser()->GetTabStripModel()->GetActiveWebContents();
  content::TestNavigationObserver navigation_watcher(
      contents, 1, content::MessageLoopRunner::QuitMode::DEFERRED);

  // Navigate without blocking.
  const GURL test_url = chrome_test_utils::GetTestUrl(
      base::FilePath(base::FilePath::kCurrentDirectory),
      base::FilePath(FILE_PATH_LITERAL("title2.html")));
  contents->GetController().LoadURL(test_url, content::Referrer(),
                                    ui::PAGE_TRANSITION_LINK, std::string());
  {
    base::RunLoop run_loop;
    browser_view()->SetLoadingAnimationStateChangeClosureForTesting(
        run_loop.QuitClosure());

    // Loading animation is not rendered when browser view is hidden.
    browser_view()->Minimize();
    run_loop.Run();
  }

  EXPECT_TRUE(browser()->GetTabStripModel()->TabsNeedLoadingUI());
  EXPECT_FALSE(browser_view()->IsLoadingAnimationRunning());

  {
    base::RunLoop run_loop;
    browser_view()->SetLoadingAnimationStateChangeClosureForTesting(
        run_loop.QuitClosure());

    // Loading animation is rendered when browser view is shown.
    browser_view()->Restore();
    run_loop.Run();
  }

  EXPECT_TRUE(browser()->GetTabStripModel()->TabsNeedLoadingUI());
  EXPECT_TRUE(browser_view()->IsLoadingAnimationRunning());

  // Now block for the navigation to complete.
  navigation_watcher.Wait();
  EXPECT_FALSE(browser()->GetTabStripModel()->TabsNeedLoadingUI());
}

// On Mac, voiceover treats tab modal dialogs as native windows, so setting an
// accessible title for tab-modal dialogs is not necessary.
#if !BUILDFLAG(IS_MAC)
namespace {

class TestTabModalConfirmDialogDelegate : public TabModalConfirmDialogDelegate {
 public:
  explicit TestTabModalConfirmDialogDelegate(content::WebContents* contents)
      : TabModalConfirmDialogDelegate(contents) {}

  TestTabModalConfirmDialogDelegate(const TestTabModalConfirmDialogDelegate&) =
      delete;
  TestTabModalConfirmDialogDelegate& operator=(
      const TestTabModalConfirmDialogDelegate&) = delete;

  std::u16string GetTitle() override { return std::u16string(u"Dialog Title"); }
  std::u16string GetDialogMessage() override { return std::u16string(); }
};

}  // namespace

// Open a tab-modal dialog and check that the accessible window title is the
// title of the dialog. The accessible window title is based on the focused
// dialog and this dependency on focus is why this is an interactive ui test.
IN_PROC_BROWSER_TEST_F(BrowserViewTest, GetAccessibleTabModalDialogTitle) {
  std::u16string window_title =
      u"about:blank - " + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
  EXPECT_TRUE(base::StartsWith(browser_view()->GetAccessibleWindowTitle(),
                               window_title, base::CompareCase::SENSITIVE));

  content::WebContents* contents = browser_view()->GetActiveWebContents();
  auto delegate = std::make_unique<TestTabModalConfirmDialogDelegate>(contents);
  TestTabModalConfirmDialogDelegate* delegate_observer = delegate.get();
  TabModalConfirmDialog::Create(std::move(delegate), contents);
  EXPECT_EQ(browser_view()->GetAccessibleWindowTitle(),
            delegate_observer->GetTitle());

  delegate_observer->Close();

  EXPECT_TRUE(base::StartsWith(browser_view()->GetAccessibleWindowTitle(),
                               window_title, base::CompareCase::SENSITIVE));
}
#endif  // !BUILDFLAG(IS_MAC)

#if BUILDFLAG(IS_CHROMEOS)
using BrowserViewLockedFullscreenTestChromeOS = BrowserViewTest;

IN_PROC_BROWSER_TEST_F(BrowserViewLockedFullscreenTestChromeOS,
                       ShowExclusiveAccessBubbleWhenNotLocked) {
  ash::PinWindow(browser()->GetWindow()->GetNativeWindow(), /*trusted=*/false);
  browser()
      ->GetFeatures()
      .exclusive_access_manager()
      ->context()
      ->UpdateExclusiveAccessBubble(
          {
              .origin = url::Origin::Create(
                  GURL("http://www.example.com")),  // Should be non-empty to
                                                    // show bubble
              .type = ExclusiveAccessBubbleType::
                  EXCLUSIVE_ACCESS_BUBBLE_TYPE_FULLSCREEN_EXIT_INSTRUCTION,
              .force_update = true,
          },
          base::NullCallback());
  EXPECT_TRUE(browser_view()
                  ->GetExclusiveAccessContext()
                  ->IsExclusiveAccessBubbleDisplayed());
}

IN_PROC_BROWSER_TEST_F(BrowserViewLockedFullscreenTestChromeOS,
                       HideExclusiveAccessBubbleWhenLocked) {
  ash::PinWindow(browser()->GetWindow()->GetNativeWindow(), /*trusted=*/true);
  browser()
      ->GetFeatures()
      .exclusive_access_manager()
      ->context()
      ->UpdateExclusiveAccessBubble(
          {.origin = url::Origin::Create(
               GURL("http://www.example.com")),  // Should be non-empty to show
                                                 // bubble
           .type = ExclusiveAccessBubbleType::
               EXCLUSIVE_ACCESS_BUBBLE_TYPE_FULLSCREEN_EXIT_INSTRUCTION,
           .force_update = true},
          base::NullCallback());
  EXPECT_FALSE(browser_view()
                   ->GetExclusiveAccessContext()
                   ->IsExclusiveAccessBubbleDisplayed());
}

IN_PROC_BROWSER_TEST_F(BrowserViewLockedFullscreenTestChromeOS,
                       EnableImmersiveModeWhenNotTrustedPinned) {
  ash::PinWindow(browser()->GetWindow()->GetNativeWindow(), /*trusted=*/false);
  EXPECT_TRUE(ImmersiveModeController::From(browser())->IsEnabled());
}

IN_PROC_BROWSER_TEST_F(BrowserViewLockedFullscreenTestChromeOS,
                       DisableImmersiveModeWhenNotLockedForOnTask) {
  ash::boca::OnTaskLockedController::From(browser())->set_locked_for_on_task(
      false);
  ash::PinWindow(browser()->GetWindow()->GetNativeWindow(), /*trusted=*/true);
  EXPECT_FALSE(ImmersiveModeController::From(browser())->IsEnabled());
}

IN_PROC_BROWSER_TEST_F(BrowserViewLockedFullscreenTestChromeOS,
                       EnableImmersiveModeWhenLockedForOnTask) {
  ash::boca::OnTaskLockedController::From(browser())->set_locked_for_on_task(
      true);
  ash::PinWindow(browser()->GetWindow()->GetNativeWindow(), /*trusted=*/true);
  EXPECT_TRUE(ImmersiveModeController::From(browser())->IsEnabled());
}
#endif  // BUILDFLAG(IS_CHROMEOS)
