// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/wm/float/float_controller.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "ash/shell.h"
#include "ash/webui/system_apps/public/system_web_app_type.h"
#include "ash/wm/float/float_controller.h"
#include "ash/wm/float/float_test_api.h"
#include "ash/wm/window_state.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/webui_tab_strip_container_view.h"
#include "chrome/test/base/ash/util/ash_test_util.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
#include "ui/aura/window.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/window_util.h"
namespace {
// Tuck a window to the bottom right corner by generating a fling.
void TuckWindow(aura::Window* window) {
auto* frame_view = static_cast<BrowserNonClientFrameViewChromeOS*>(
views::Widget::GetWidgetForNativeView(window)
->non_client_view()
->frame_view());
const gfx::Point start =
frame_view->GetBoundsInScreen().top_center() + gfx::Vector2d(0, 10);
const gfx::Vector2d offset(10, 10);
ui::test::EventGenerator event_generator(window->GetRootWindow());
event_generator.GestureTapAt(start);
event_generator.GestureScrollSequence(start, start + offset,
base::Milliseconds(10), /*steps=*/1);
}
} // namespace
using FloatControllerBrowserTest = InProcessBrowserTest;
// Tests that repeated tucking of a floated window in tablet mode does not cause
// the window to freeze. Regression test for b/278917878.
IN_PROC_BROWSER_TEST_F(FloatControllerBrowserTest,
TuckingBrowserDoesNotFreezeWindow) {
ash::test::InstallSystemAppsForTesting(browser()->profile());
// Open two SWAs. The bug was a result of the window targeters installed by
// the window tucker and immersive mode not being reinstalled in the correct
// order. More details in b/278917878.
ash::test::CreateSystemWebApp(browser()->profile(),
ash::SystemWebAppType::FILE_MANAGER);
aura::Window* browser_window1 =
BrowserList::GetInstance()->GetLastActive()->window()->GetNativeWindow();
ash::test::CreateSystemWebApp(browser()->profile(),
ash::SystemWebAppType::SETTINGS);
aura::Window* browser_window2 =
BrowserList::GetInstance()->GetLastActive()->window()->GetNativeWindow();
ASSERT_NE(browser()->window()->GetNativeWindow(), browser_window1);
ASSERT_NE(browser()->window()->GetNativeWindow(), browser_window2);
ASSERT_NE(browser_window1, browser_window2);
auto* float_controller = ash::Shell::Get()->float_controller();
ash::ShellTestApi().SetTabletModeEnabledForTest(true);
// Float and then tuck the background window repeatedly. This emulates the
// steps listed in the bug.
ui::test::EventGenerator event_generator(browser_window1->GetRootWindow());
event_generator.PressAndReleaseKeyAndModifierKeys(
ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
ASSERT_TRUE(ash::WindowState::Get(browser_window2)->IsFloated());
TuckWindow(browser_window2);
ash::ShellTestApi().WaitForWindowFinishAnimating(browser_window2);
ASSERT_TRUE(
float_controller->IsFloatedWindowTuckedForTablet(browser_window2));
// Float `browser_window1` using accelerator and tuck it.
wm::ActivateWindow(browser_window1);
event_generator.PressAndReleaseKeyAndModifierKeys(
ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
ASSERT_TRUE(ash::WindowState::Get(browser_window1)->IsFloated());
ASSERT_TRUE(ash::WindowState::Get(browser_window2)->IsMaximized());
TuckWindow(browser_window1);
ash::ShellTestApi().WaitForWindowFinishAnimating(browser_window2);
ASSERT_TRUE(
float_controller->IsFloatedWindowTuckedForTablet(browser_window1));
// Float `browser_window2` using accelerator and tuck it. At each point,
// `TuckWindow` should tuck the window otherwise the window has frozen and the
// test will hang.
wm::ActivateWindow(browser_window2);
event_generator.PressAndReleaseKeyAndModifierKeys(
ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
ASSERT_TRUE(ash::WindowState::Get(browser_window2)->IsFloated());
ASSERT_TRUE(ash::WindowState::Get(browser_window1)->IsMaximized());
TuckWindow(browser_window2);
ash::ShellTestApi().WaitForWindowFinishAnimating(browser_window2);
ASSERT_TRUE(
float_controller->IsFloatedWindowTuckedForTablet(browser_window2));
}
// Tests that flinging down from the client area to move a floated window does
// not open the web ui tab strip.
IN_PROC_BROWSER_TEST_F(FloatControllerBrowserTest,
FlingDownDoesNotOpenTabStrip) {
ash::ShellTestApi().SetTabletModeEnabledForTest(true);
// A floated window is magnetized to the bottom right by default.
aura::Window* window = browser()->window()->GetNativeWindow();
ui::test::EventGenerator event_generator(window->GetRootWindow(), window);
event_generator.PressAndReleaseKeyAndModifierKeys(
ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
ASSERT_TRUE(ash::WindowState::Get(window)->IsFloated());
ASSERT_EQ(ash::FloatController::MagnetismCorner::kBottomRight,
ash::FloatTestApi::GetMagnetismCornerForBounds(
window->GetBoundsInScreen()));
// Drag the window up to the top right.
auto get_draggable_point = [](aura::Window* window) {
return gfx::Point(window->GetBoundsInScreen().CenterPoint().x(),
window->GetBoundsInScreen().y() + 10);
};
event_generator.set_current_screen_location(get_draggable_point(window));
event_generator.PressMoveAndReleaseTouchBy(0, -200);
ASSERT_EQ(ash::FloatController::MagnetismCorner::kTopRight,
ash::FloatTestApi::GetMagnetismCornerForBounds(
window->GetBoundsInScreen()));
// Drag the window back down. Test that it doesn't open the tab strip.
event_generator.set_current_screen_location(get_draggable_point(window));
event_generator.PressMoveAndReleaseTouchBy(0, 200);
ASSERT_EQ(ash::FloatController::MagnetismCorner::kBottomRight,
ash::FloatTestApi::GetMagnetismCornerForBounds(
window->GetBoundsInScreen()));
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
EXPECT_FALSE(browser_view->webui_tab_strip()->GetVisible());
}