| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h" |
| |
| #include "base/test/task_environment.h" |
| #include "base/test/test_mock_time_task_runner.h" |
| #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h" |
| #include "chrome/browser/ui/exclusive_access/exclusive_access_test.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/input/native_web_keyboard_event.h" |
| #include "content/public/test/browser_test.h" |
| #include "ui/base/ozone_buildflags.h" |
| #include "ui/events/keycodes/keyboard_codes.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| using ExclusiveAccessManagerTest = ExclusiveAccessTest; |
| |
| IN_PROC_BROWSER_TEST_F(ExclusiveAccessManagerTest, HandleKeyEvent_NonEscKey) { |
| // Non-Esc key events should be ignored. |
| input::NativeWebKeyboardEvent event( |
| blink::WebInputEvent::Type::kRawKeyDown, |
| blink::WebInputEvent::kNoModifiers, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| event.windows_key_code = ui::VKEY_LEFT; |
| EXPECT_FALSE(GetExclusiveAccessManager()->HandleUserKeyEvent(event)); |
| ExpectMockControllerReceivedEscape(0); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExclusiveAccessManagerTest, |
| HandleKeyEvent_PointerLocked) { |
| // Esc key pressed while pointer is locked should be handled. |
| RequestToLockPointer(/*user_gesture=*/true, |
| /*last_unlocked_by_target=*/false); |
| EXPECT_TRUE(SendEscapeToExclusiveAccessManager()); |
| ASSERT_FALSE(GetExclusiveAccessManager() |
| ->pointer_lock_controller() |
| ->IsPointerLocked()); |
| EXPECT_FALSE(SendEscapeToExclusiveAccessManager()); |
| ExpectMockControllerReceivedEscape(2); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExclusiveAccessManagerTest, |
| HandleKeyEvent_TabFullscreen) { |
| // Esc key pressed while in fullscreen mode should be handled. |
| EnterActiveTabFullscreen(); |
| EXPECT_TRUE(SendEscapeToExclusiveAccessManager()); |
| WaitForTabFullscreenExit(); |
| EXPECT_FALSE(SendEscapeToExclusiveAccessManager()); |
| ExpectMockControllerReceivedEscape(2); |
| } |
| |
| // TODO: crbug.com/352244303 - For some reason the test fails on |
| // linux_wayland_rel when kKeyboardAndPointerLockPrompt is disabled. Re-enable |
| // the test when the feature is enabled by default. |
| #if BUILDFLAG(IS_OZONE_WAYLAND) |
| #define MAYBE_HandleKeyEvent_KeyboardLocked \ |
| DISABLED_HandleKeyEvent_KeyboardLocked |
| #else |
| #define MAYBE_HandleKeyEvent_KeyboardLocked HandleKeyEvent_KeyboardLocked |
| #endif |
| IN_PROC_BROWSER_TEST_F(ExclusiveAccessManagerTest, |
| MAYBE_HandleKeyEvent_KeyboardLocked) { |
| // Esc key pressed while keyboard is locked without Esc key should be handled. |
| EnterActiveTabFullscreen(); |
| RequestKeyboardLock(/*esc_key_locked=*/false); |
| EXPECT_TRUE(SendEscapeToExclusiveAccessManager()); |
| WaitForTabFullscreenExit(); |
| ASSERT_FALSE(GetExclusiveAccessManager() |
| ->keyboard_lock_controller() |
| ->IsKeyboardLockActive()); |
| EXPECT_FALSE(SendEscapeToExclusiveAccessManager()); |
| ExpectMockControllerReceivedEscape(2); |
| |
| // Esc key pressed while keyboard is locked with Esc key should not be |
| // handled. |
| EnterActiveTabFullscreen(); |
| RequestKeyboardLock(/*esc_key_locked=*/true); |
| EXPECT_FALSE(SendEscapeToExclusiveAccessManager()); |
| ASSERT_TRUE(GetExclusiveAccessManager() |
| ->keyboard_lock_controller() |
| ->IsKeyboardLockActive()); |
| ASSERT_TRUE(IsWindowFullscreenForTabOrPending()); |
| ExpectMockControllerReceivedEscape(0); |
| } |
| |
| class ExclusiveAccessManagerPressAndHoldEscTest : public ExclusiveAccessTest { |
| public: |
| ExclusiveAccessManagerPressAndHoldEscTest() { |
| scoped_feature_list_.InitAndEnableFeature( |
| features::kPressAndHoldEscToExitBrowserFullscreen); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ExclusiveAccessManagerPressAndHoldEscTest, |
| HoldTimerStartOnEscKeyPressWithModifiers) { |
| // Don't start the timer on Esc key down with a non-stateful modifier. |
| input::NativeWebKeyboardEvent event( |
| blink::WebInputEvent::Type::kRawKeyDown, blink::WebInputEvent::kShiftKey, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| event.windows_key_code = ui::VKEY_ESCAPE; |
| GetExclusiveAccessManager()->HandleUserKeyEvent(event); |
| EXPECT_FALSE(IsEscKeyHoldTimerRunning()); |
| |
| // Start the timer on Esc key down with a stateful modifier. |
| event.SetModifiers(blink::WebInputEvent::kNumLockOn); |
| GetExclusiveAccessManager()->HandleUserKeyEvent(event); |
| EXPECT_TRUE(IsEscKeyHoldTimerRunning()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExclusiveAccessManagerPressAndHoldEscTest, |
| HandlePressAndHoldKeyEvent) { |
| // Start the timer on key down event. |
| SendEscapeToExclusiveAccessManager(/*is_key_down=*/true); |
| EXPECT_TRUE(IsEscKeyHoldTimerRunning()); |
| |
| // Multiple key down events won't affect the timer. |
| SendEscapeToExclusiveAccessManager(/*is_key_down=*/true); |
| EXPECT_TRUE(IsEscKeyHoldTimerRunning()); |
| |
| // Stop the timer on key up event. |
| EXPECT_CALL(*mock_controller(), HandleUserReleasedEscapeEarly()); |
| SendEscapeToExclusiveAccessManager(/*is_key_down=*/false); |
| EXPECT_FALSE(IsEscKeyHoldTimerRunning()); |
| |
| // Restart the timer and fastforward the clock to trigger the timer. |
| { |
| auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); |
| base::TestMockTimeTaskRunner::ScopedContext scoped_context( |
| task_runner.get()); |
| |
| SendEscapeToExclusiveAccessManager(/*is_key_down=*/true); |
| EXPECT_TRUE(IsEscKeyHoldTimerRunning()); |
| |
| EXPECT_CALL(*mock_controller(), HandleUserHeldEscape()); |
| task_runner->FastForwardBy(base::Seconds(2)); |
| EXPECT_FALSE(IsEscKeyHoldTimerRunning()); |
| } |
| |
| // Timer won't start on key up event. |
| SendEscapeToExclusiveAccessManager(/*is_key_down=*/false); |
| EXPECT_FALSE(IsEscKeyHoldTimerRunning()); |
| } |
| |
| // Disable the test on ChromeOS because the Exclusive Access Bubble isn't shown |
| // for browser fullscreen. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_ShowExclusiveAccessBubble DISABLED_ShowExclusiveAccessBubble |
| #else |
| #define MAYBE_ShowExclusiveAccessBubble ShowExclusiveAccessBubble |
| #endif // IS_CHROMEOS |
| IN_PROC_BROWSER_TEST_F(ExclusiveAccessManagerPressAndHoldEscTest, |
| MAYBE_ShowExclusiveAccessBubble) { |
| // The bubble is shown after the browser enters fullscreen. |
| ui_test_utils::ToggleFullscreenModeAndWait(browser()); |
| EXPECT_TRUE(IsFullscreenForBrowser()); |
| EXPECT_TRUE(IsExclusiveAccessBubbleDisplayed()); |
| |
| // Setting the bubble type to none will hide the bubble. |
| GetExclusiveAccessManager()->context()->UpdateExclusiveAccessBubble( |
| {.force_update = true}, base::NullCallback()); |
| EXPECT_FALSE(IsExclusiveAccessBubbleDisplayed()); |
| |
| // The bubble is not shown after a short press on Esc key. |
| SendEscapeToExclusiveAccessManager(/*is_key_down=*/true); |
| SendEscapeToExclusiveAccessManager(/*is_key_down=*/false); |
| EXPECT_FALSE(IsExclusiveAccessBubbleDisplayed()); |
| |
| { |
| auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); |
| base::TestMockTimeTaskRunner::ScopedContext scoped_context( |
| task_runner.get()); |
| |
| // The bubble is shown after pressing on the Esc key for 0.5 second. |
| SendEscapeToExclusiveAccessManager(/*is_key_down=*/true); |
| task_runner->FastForwardBy(base::Seconds(0.5)); |
| EXPECT_TRUE(IsExclusiveAccessBubbleDisplayed()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExclusiveAccessManagerTest, |
| GetOriginForFullscreenBubble) { |
| const GURL kTestUrl("https://example.com"); |
| const url::Origin kTestOrigin = url::Origin::Create(kTestUrl); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kTestUrl)); |
| EXPECT_FALSE(GetExclusiveAccessManager() |
| ->context() |
| ->IsExclusiveAccessBubbleDisplayed()); |
| |
| // Enter fullscreen |
| EnterActiveTabFullscreen(); |
| EXPECT_EQ(kTestOrigin, |
| GetExclusiveAccessManager()->GetExclusiveAccessBubbleOrigin()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExclusiveAccessManagerTest, |
| GetOriginForPointerLockBubble) { |
| const GURL kTestUrl("https://example.com"); |
| const url::Origin kTestOrigin = url::Origin::Create(kTestUrl); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kTestUrl)); |
| EXPECT_FALSE(GetExclusiveAccessManager() |
| ->context() |
| ->IsExclusiveAccessBubbleDisplayed()); |
| |
| RequestToLockPointer(/*user_gesture=*/true, |
| /*last_unlocked_by_target=*/false); |
| EXPECT_EQ(kTestOrigin, |
| GetExclusiveAccessManager()->GetExclusiveAccessBubbleOrigin()); |
| } |