blob: cb9e5d78f7826acb39ae2fd0d937fa3dceeea2e8 [file] [log] [blame]
// 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 <memory>
#include "ash/shell.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/ash/app_mode/kiosk_controller.h"
#include "chrome/browser/ash/app_mode/kiosk_system_session.h"
#include "chrome/browser/ash/app_mode/test/kiosk_mixin.h"
#include "chrome/browser/ash/app_mode/test/kiosk_test_utils.h"
#include "chrome/browser/chromeos/app_mode/kiosk_browser_window_handler.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/policy/developer_tools_policy_handler.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/task_manager_view.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/window_open_disposition.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
using policy::DeveloperToolsPolicyHandler::Availability::kAllowed;
using policy::DeveloperToolsPolicyHandler::Availability::kDisallowed;
namespace ash {
using kiosk::test::CreatePopupBrowser;
using kiosk::test::CreateRegularBrowser;
using kiosk::test::CurrentProfile;
using kiosk::test::DidKioskCloseNewWindow;
using kiosk::test::WaitKioskLaunched;
namespace {
KioskSystemSession& GetKioskSystemSession() {
return CHECK_DEREF(KioskController::Get().GetKioskSystemSession());
}
} // namespace
// Test kiosk troubleshooting tools on web kiosk.
class KioskTroubleshootingToolsTest : public MixinBasedInProcessBrowserTest {
public:
KioskTroubleshootingToolsTest() = default;
KioskTroubleshootingToolsTest(const KioskTroubleshootingToolsTest&) = delete;
KioskTroubleshootingToolsTest& operator=(
const KioskTroubleshootingToolsTest&) = delete;
void SetUpOnMainThread() override {
MixinBasedInProcessBrowserTest::SetUpOnMainThread();
event_generator_ = std::make_unique<ui::test::EventGenerator>(
ash::Shell::Get()->GetPrimaryRootWindow());
ASSERT_TRUE(WaitKioskLaunched());
SelectFirstBrowser();
ExpectOnlyKioskAppOpen();
}
void UpdateTroubleshootingToolsPolicy(bool enable) const {
CurrentProfile().GetPrefs()->SetBoolean(
prefs::kKioskTroubleshootingToolsEnabled, enable);
}
void EnableDevTools() const {
CurrentProfile().GetPrefs()->SetInteger(prefs::kDevToolsAvailability,
static_cast<int>(kAllowed));
}
void ExpectOpenBrowser(chromeos::KioskBrowserWindowType window_type) const {
EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
histogram.ExpectBucketCount(chromeos::kKioskNewBrowserWindowHistogram,
window_type, 1);
histogram.ExpectTotalCount(chromeos::kKioskNewBrowserWindowHistogram, 1);
}
void ExpectOnlyKioskAppOpen() const {
// The initial browser should exist in the web kiosk session.
ASSERT_EQ(BrowserList::GetInstance()->size(), 1u);
Browser* kiosk_browser = BrowserList::GetInstance()->get(0);
ASSERT_EQ(kiosk_browser->tab_strip_model()->count(), 1);
content::WebContents* contents =
kiosk_browser->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(contents);
if (contents->IsLoading()) {
content::WaitForLoadStop(contents);
}
ASSERT_EQ(
contents->GetLastCommittedURL(),
kiosk_.GetDefaultServerUrl(KioskMixin::SimpleWebAppOption().url_path));
}
void EmulateOpenNewWindowShortcutPressed() const {
// Ctrl + N
ui::test::EmulateFullKeyPressReleaseSequence(
event_generator_.get(), ui::VKEY_N, /*control=*/true, /*shift=*/false,
/*alt=*/false, /*command=*/false);
}
Browser* EmulateOpenNewWindowShortcutPressedAndReturnNewBrowser() const {
EmulateOpenNewWindowShortcutPressed();
EXPECT_FALSE(DidKioskCloseNewWindow());
return BrowserList::GetInstance()->GetLastActive();
}
void EmulateOpenTaskManagerShortcutPressed() const {
// Esc + Search
ui::test::EmulateFullKeyPressReleaseSequence(
event_generator_.get(), ui::VKEY_ESCAPE, /*control=*/false,
/*shift=*/false,
/*alt=*/false, /*command=*/true);
}
void EmulateSwitchWindowsForwardShortcutPressed() const {
// Alt+Tab
ui::test::EmulateFullKeyPressReleaseSequence(
event_generator_.get(), ui::VKEY_TAB, /*control=*/false,
/*shift=*/false,
/*alt=*/true, /*command=*/false);
}
void EmulateSwitchWindowsBackwardShortcutPressed() const {
// Shift+Alt+Tab
ui::test::EmulateFullKeyPressReleaseSequence(
event_generator_.get(), ui::VKEY_TAB, /*control=*/false,
/*shift=*/false,
/*alt=*/true, /*command=*/false);
}
Browser& OpenForAppPopupBrowser() const {
CurrentProfile().GetPrefs()->SetBoolean(prefs::kNewWindowsInKioskAllowed,
true);
Browser& popup_browser =
CreatePopupBrowser(CurrentProfile(), browser()->app_name());
EXPECT_FALSE(DidKioskCloseNewWindow());
return popup_browser;
}
bool IsLactActiveBrowserResizable() {
BrowserWindow* lact_active_window =
BrowserList::GetInstance()->GetLastActive()->window();
views::Widget* widget = views::Widget::GetWidgetForNativeWindow(
lact_active_window->GetNativeWindow());
return widget->widget_delegate()->CanResize();
}
task_manager::TaskManagerView* GetTaskManagerView() const {
return task_manager::TaskManagerView::GetInstanceForTests();
}
protected:
std::unique_ptr<ui::test::EventGenerator> event_generator_;
base::HistogramTester histogram;
KioskMixin kiosk_{
&mixin_host_,
KioskMixin::Config{/*name=*/{},
KioskMixin::AutoLaunchAccount{
KioskMixin::SimpleWebAppOption().account_id},
{KioskMixin::SimpleWebAppOption()}}};
};
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
DevToolsBasicShowAndShutdown) {
UpdateTroubleshootingToolsPolicy(/*enable=*/true);
EnableDevTools();
DevToolsWindowTesting::OpenDevToolsWindowSync(browser(),
/*is_docked=*/false);
ExpectOpenBrowser(chromeos::KioskBrowserWindowType::kOpenedDevToolsBrowser);
// Shut down the session when kiosk troubleshooting tools get disabled.
UpdateTroubleshootingToolsPolicy(/*enable=*/false);
EXPECT_TRUE(GetKioskSystemSession().is_shutting_down());
}
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
DevToolsDisallowedNoShow) {
UpdateTroubleshootingToolsPolicy(/*enable=*/true);
// Devtools are not enabled, but disabled by default.
DevToolsWindowTesting::OpenDevToolsWindowSync(browser(),
/*is_docked=*/false);
ExpectOnlyKioskAppOpen();
histogram.ExpectTotalCount(chromeos::kKioskNewBrowserWindowHistogram, 0);
}
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
DevToolsTroubleshootingDisabled) {
EnableDevTools();
DevToolsWindowTesting::OpenDevToolsWindowSync(browser(),
/*is_docked=*/false);
// Since the devtools are allowed, the devtools window is open, but
// immediately gets closed, since the kiosk troubleshooting tools are
// disabled by the kiosk policy.
histogram.ExpectBucketCount(
chromeos::kKioskNewBrowserWindowHistogram,
chromeos::KioskBrowserWindowType::kClosedRegularBrowser, 1);
histogram.ExpectTotalCount(chromeos::kKioskNewBrowserWindowHistogram, 1);
}
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
NewWindowBasicShowAndShutdown) {
UpdateTroubleshootingToolsPolicy(/*enable=*/true);
EmulateOpenNewWindowShortcutPressed();
EXPECT_FALSE(DidKioskCloseNewWindow());
ExpectOpenBrowser(
chromeos::KioskBrowserWindowType::kOpenedTroubleshootingNormalBrowser);
// Shut down the session when kiosk troubleshooting tools get disabled.
UpdateTroubleshootingToolsPolicy(/*enable=*/false);
EXPECT_TRUE(GetKioskSystemSession().is_shutting_down());
}
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
OpenAllTroubleshootingTools) {
UpdateTroubleshootingToolsPolicy(/*enable=*/true);
EnableDevTools();
DevToolsWindowTesting::OpenDevToolsWindowSync(browser(),
/*is_docked=*/false);
EmulateOpenNewWindowShortcutPressed();
EXPECT_FALSE(DidKioskCloseNewWindow());
EXPECT_EQ(BrowserList::GetInstance()->size(), 3u);
histogram.ExpectBucketCount(
chromeos::kKioskNewBrowserWindowHistogram,
chromeos::KioskBrowserWindowType::kOpenedDevToolsBrowser, 1);
histogram.ExpectBucketCount(
chromeos::kKioskNewBrowserWindowHistogram,
chromeos::KioskBrowserWindowType::kOpenedTroubleshootingNormalBrowser, 1);
histogram.ExpectTotalCount(chromeos::kKioskNewBrowserWindowHistogram, 2);
}
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
AllTroubleshootingToolsAreResizable) {
UpdateTroubleshootingToolsPolicy(/*enable=*/true);
EnableDevTools();
// The main browser should not be resizable.
EXPECT_FALSE(IsLactActiveBrowserResizable());
DevToolsWindowTesting::OpenDevToolsWindowSync(browser(),
/*is_docked=*/false);
EXPECT_TRUE(IsLactActiveBrowserResizable());
EmulateOpenNewWindowShortcutPressed();
EXPECT_FALSE(DidKioskCloseNewWindow());
EXPECT_TRUE(IsLactActiveBrowserResizable());
}
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
NewWindowDisallowedNoShow) {
// Explicitly open a new window to make sure it will be closed.
CreateRegularBrowser(CurrentProfile());
EXPECT_TRUE(DidKioskCloseNewWindow());
histogram.ExpectBucketCount(
chromeos::kKioskNewBrowserWindowHistogram,
chromeos::KioskBrowserWindowType::kClosedRegularBrowser, 1);
histogram.ExpectTotalCount(chromeos::kKioskNewBrowserWindowHistogram, 1);
}
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
NewWindowShortcutDisallowed) {
EmulateOpenNewWindowShortcutPressed();
base::RunLoop().RunUntilIdle();
ExpectOnlyKioskAppOpen();
// Since new window shortcut will not be proceed at all, `KioskBrowserSession`
// will not handle a new window because it was never created.
histogram.ExpectBucketCount(
chromeos::kKioskNewBrowserWindowHistogram,
chromeos::KioskBrowserWindowType::kClosedRegularBrowser, 0);
histogram.ExpectTotalCount(chromeos::kKioskNewBrowserWindowHistogram, 0);
}
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest, NewWindowAddTab) {
UpdateTroubleshootingToolsPolicy(/*enable=*/true);
Browser* newly_opened_browser =
EmulateOpenNewWindowShortcutPressedAndReturnNewBrowser();
ExpectOpenBrowser(
chromeos::KioskBrowserWindowType::kOpenedTroubleshootingNormalBrowser);
int initial_number_of_tabs = newly_opened_browser->tab_strip_model()->count();
ui_test_utils::NavigateToURLWithDisposition(
newly_opened_browser, GURL("https://www.google.com/"),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
EXPECT_EQ(newly_opened_browser->tab_strip_model()->count(),
initial_number_of_tabs + 1);
}
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest, SwitchWindowsForward) {
UpdateTroubleshootingToolsPolicy(/*enable=*/true);
EXPECT_TRUE(browser()->window()->IsActive());
Browser* newly_opened_browser =
EmulateOpenNewWindowShortcutPressedAndReturnNewBrowser();
// When new window is opened, it becomes active.
EXPECT_TRUE(newly_opened_browser->window()->IsActive());
EXPECT_FALSE(browser()->window()->IsActive());
EmulateSwitchWindowsForwardShortcutPressed();
// The main window should be active again.
EXPECT_TRUE(browser()->window()->IsActive());
EXPECT_FALSE(newly_opened_browser->window()->IsActive());
}
// TODO(crbug.com/1481017): Re-enable this test
#if defined(MEMORY_SANITIZER)
#define MAYBE_SwitchWindowsBackward DISABLED_SwitchWindowsBackward
#else
#define MAYBE_SwitchWindowsBackward SwitchWindowsBackward
#endif
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
MAYBE_SwitchWindowsBackward) {
UpdateTroubleshootingToolsPolicy(/*enable=*/true);
EXPECT_TRUE(browser()->window()->IsActive());
Browser* newly_opened_browser =
EmulateOpenNewWindowShortcutPressedAndReturnNewBrowser();
// When new window is opened, it becomes active.
EXPECT_TRUE(newly_opened_browser->window()->IsActive());
EXPECT_FALSE(browser()->window()->IsActive());
EmulateSwitchWindowsBackwardShortcutPressed();
// The main window should be active again.
EXPECT_TRUE(browser()->window()->IsActive());
EXPECT_FALSE(newly_opened_browser->window()->IsActive());
}
IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest, SwitchWindowsDisallowed) {
EXPECT_TRUE(browser()->window()->IsActive());
// Enable another feature to allow opening two popup browsers to make sure
// that switching between windows is still not available if the
// troubleshooting policy is disabled.
Browser& new_browser = OpenForAppPopupBrowser();
// When new window is opened, it becomes active.
EXPECT_TRUE(new_browser.window()->IsActive());
EXPECT_FALSE(browser()->window()->IsActive());
EmulateSwitchWindowsForwardShortcutPressed();
// Active window remains the same.
EXPECT_TRUE(new_browser.window()->IsActive());
EXPECT_FALSE(browser()->window()->IsActive());
EmulateSwitchWindowsBackwardShortcutPressed();
// Active window remains the same.
EXPECT_TRUE(new_browser.window()->IsActive());
EXPECT_FALSE(browser()->window()->IsActive());
}
IN_PROC_BROWSER_TEST_F(
KioskTroubleshootingToolsTest,
TaskManagerShortcutShouldNotShowIfTroubleshootingIsDisabled) {
EmulateOpenTaskManagerShortcutPressed();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(nullptr, GetTaskManagerView());
}
IN_PROC_BROWSER_TEST_F(
KioskTroubleshootingToolsTest,
TaskManagerShortcutShouldShowIfTroubleshootingIsEnabled) {
UpdateTroubleshootingToolsPolicy(/*enable=*/true);
EmulateOpenTaskManagerShortcutPressed();
base::RunLoop().RunUntilIdle();
EXPECT_NE(nullptr, GetTaskManagerView());
}
} // namespace ash