blob: 345d05785dda0044791b7c75961409cdc51ec9be [file] [log] [blame]
// Copyright 2012 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_test.h"
#include <optional>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/run_loop.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_bubble.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/exclusive_access/keyboard_lock_controller.h"
#include "chrome/browser/ui/exclusive_access/pointer_lock_controller.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/exclusive_access_bubble_views.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/input/native_web_keyboard_event.h"
#include "content/public/browser/permission_result.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/mock_permission_controller.h"
#include "content/public/test/test_navigation_observer.h"
#include "exclusive_access_controller_base.h"
#include "exclusive_access_manager.h"
#include "exclusive_access_test.h"
#include "extensions/common/extension.h"
#include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
#include "ui/base/ui_base_features.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/animation/animation_test_api.h"
using content::WebContents;
const char ExclusiveAccessTest::kFullscreenKeyboardLockHTML[] =
"/fullscreen_keyboardlock/fullscreen_keyboardlock.html";
const char ExclusiveAccessTest::kFullscreenPointerLockHTML[] =
"/fullscreen_pointerlock/fullscreen_pointerlock.html";
MockExclusiveAccessController::MockExclusiveAccessController(
ExclusiveAccessManager* manager)
: ExclusiveAccessControllerBase(manager) {}
MockExclusiveAccessController::~MockExclusiveAccessController() = default;
bool MockExclusiveAccessController::HandleUserPressedEscape() {
escape_pressed_count_++;
return false;
}
ExclusiveAccessTest::ExclusiveAccessTest() {
// It is important to disable system keyboard lock as low-level test utilities
// may install a keyboard hook to listen for keyboard events and having an
// active system hook may cause issues with that mechanism.
scoped_feature_list_.InitWithFeatures({}, {features::kSystemKeyboardLock});
}
ExclusiveAccessTest::~ExclusiveAccessTest() = default;
void ExclusiveAccessTest::SetUpOnMainThread() {
permission_controller_ =
std::make_unique<content::MockPermissionController>();
ON_CALL(*permission_controller_, RequestPermissionsFromCurrentDocument)
.WillByDefault(
[](content::RenderFrameHost* render_frame_host,
content::PermissionRequestDescription request_description,
base::OnceCallback<void(
const std::vector<content::PermissionResult>&)> callback) {
std::move(callback).Run(std::vector<content::PermissionResult>(
request_description.permissions.size(),
content::PermissionResult(
content::PermissionStatus::GRANTED,
content::PermissionStatusSource::UNSPECIFIED)));
});
GetExclusiveAccessManager()
->permission_manager()
.set_permission_controller_for_test(permission_controller_.get());
GetExclusiveAccessManager()
->pointer_lock_controller()
->bubble_hide_callback_for_test_ = base::BindRepeating(
&ExclusiveAccessTest::OnBubbleHidden, weak_ptr_factory_.GetWeakPtr(),
&pointer_lock_bubble_hide_reason_recorder_);
GetExclusiveAccessManager()
->keyboard_lock_controller()
->bubble_hide_callback_for_test_ = base::BindRepeating(
&ExclusiveAccessTest::OnBubbleHidden, weak_ptr_factory_.GetWeakPtr(),
&keyboard_lock_bubble_hide_reason_recorder_);
mock_controller_ = std::make_unique<MockExclusiveAccessController>(
GetExclusiveAccessManager());
GetExclusiveAccessManager()->exclusive_access_controllers_for_test().insert(
mock_controller_.get());
}
void ExclusiveAccessTest::TearDownOnMainThread() {
GetExclusiveAccessManager()
->pointer_lock_controller()
->bubble_hide_callback_for_test_ =
base::RepeatingCallback<void(ExclusiveAccessBubbleHideReason)>();
GetExclusiveAccessManager()
->keyboard_lock_controller()
->bubble_hide_callback_for_test_ =
base::RepeatingCallback<void(ExclusiveAccessBubbleHideReason)>();
GetExclusiveAccessManager()->exclusive_access_controllers_for_test().erase(
mock_controller_.get());
mock_controller_.reset();
}
// static
bool ExclusiveAccessTest::IsBubbleDownloadNotification(
ExclusiveAccessBubble* bubble) {
return bubble->params_.has_download;
}
bool ExclusiveAccessTest::RequestKeyboardLock(bool esc_key_locked) {
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
// If the caller defines |esc_key_locked| as true then we create a set of
// locked keys which includes the escape key, this will require the user/test
// to press and hold escape to exit fullscreen. If |esc_key_locked| is false,
// then we create a set of keys that does not include escape (we arbitrarily
// chose the 'a' key) which means the user/test can just press escape to exit
// fullscreen.
std::optional<base::flat_set<ui::DomCode>> codes;
if (esc_key_locked) {
codes = base::flat_set<ui::DomCode>({ui::DomCode::ESCAPE});
} else {
codes = base::flat_set<ui::DomCode>({ui::DomCode::US_A});
}
bool success = false;
base::RunLoop run_loop;
base::OnceCallback<void(blink::mojom::KeyboardLockRequestResult)> callback =
base::BindOnce(
[](bool* success, base::RunLoop* run_loop,
blink::mojom::KeyboardLockRequestResult result) {
*success =
result == blink::mojom::KeyboardLockRequestResult::kSuccess;
run_loop->Quit();
},
&success, &run_loop);
content::RequestKeyboardLock(tab, std::move(codes), std::move(callback));
run_loop.Run();
return success;
}
void ExclusiveAccessTest::RequestToLockPointer(bool user_gesture,
bool last_unlocked_by_target) {
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
PointerLockController* pointer_lock_controller =
GetExclusiveAccessManager()->pointer_lock_controller();
pointer_lock_controller->fake_pointer_lock_for_test_ = true;
base::RunLoop run_loop;
pointer_lock_controller->set_lock_state_callback_for_test(
run_loop.QuitClosure());
browser()->RequestPointerLock(tab, user_gesture, last_unlocked_by_target);
run_loop.Run();
pointer_lock_controller->fake_pointer_lock_for_test_ = false;
}
void ExclusiveAccessTest::SetWebContentsGrantedSilentPointerLockPermission() {
GetExclusiveAccessManager()
->pointer_lock_controller()
->web_contents_granted_silent_pointer_lock_permission_ =
browser()->tab_strip_model()->GetActiveWebContents();
}
void ExclusiveAccessTest::CancelKeyboardLock() {
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
content::CancelKeyboardLock(tab);
}
void ExclusiveAccessTest::LostPointerLock() {
browser()->LostPointerLock();
}
bool ExclusiveAccessTest::SendEscapeToExclusiveAccessManager(bool is_key_down) {
input::NativeWebKeyboardEvent event(
is_key_down ? blink::WebInputEvent::Type::kRawKeyDown
: blink::WebInputEvent::Type::kKeyUp,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests());
event.windows_key_code = ui::VKEY_ESCAPE;
return GetExclusiveAccessManager()->HandleUserKeyEvent(event);
}
bool ExclusiveAccessTest::IsFullscreenForBrowser() {
return GetFullscreenController()->IsFullscreenForBrowser();
}
bool ExclusiveAccessTest::IsWindowFullscreenForTabOrPending() {
return GetFullscreenController()->IsWindowFullscreenForTabOrPending();
}
void ExclusiveAccessTest::GoBack() {
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents(), 1);
chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
observer.Wait();
}
void ExclusiveAccessTest::Reload() {
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents(), 1);
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
observer.Wait();
}
void ExclusiveAccessTest::EnterActiveTabFullscreen() {
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
ui_test_utils::FullscreenWaiter waiter(browser(), {.tab_fullscreen = true});
browser()->EnterFullscreenModeForTab(tab->GetPrimaryMainFrame(), {});
waiter.Wait();
}
void ExclusiveAccessTest::WaitForTabFullscreenExit() {
ui_test_utils::FullscreenWaiter waiter(browser(), {.tab_fullscreen = false});
waiter.Wait();
}
void ExclusiveAccessTest::WaitAndVerifyFullscreenState(bool browser_fullscreen,
bool tab_fullscreen) {
ui_test_utils::FullscreenWaiter waiter(
browser(), {.browser_fullscreen = browser_fullscreen,
.tab_fullscreen = tab_fullscreen});
waiter.Wait();
}
void ExclusiveAccessTest::EnterExtensionInitiatedFullscreen() {
ui_test_utils::FullscreenWaiter waiter(browser(),
{.browser_fullscreen = true});
static const char kExtensionId[] = "extension-id";
browser()->ToggleFullscreenModeWithExtension(
extensions::Extension::GetBaseURLFromExtensionId(kExtensionId));
waiter.Wait();
}
bool ExclusiveAccessTest::IsEscKeyHoldTimerRunning() {
return GetExclusiveAccessManager()->esc_key_hold_timer_for_test().IsRunning();
}
ExclusiveAccessBubbleType ExclusiveAccessTest::GetExclusiveAccessBubbleType() {
return GetExclusiveAccessManager()->GetExclusiveAccessExitBubbleType();
}
ExclusiveAccessBubbleViews*
ExclusiveAccessTest::GetExclusiveAccessBubbleView() {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
return browser_view ? browser_view->exclusive_access_bubble() : nullptr;
}
bool ExclusiveAccessTest::IsExclusiveAccessBubbleDisplayed() {
return GetExclusiveAccessManager()
->context()
->IsExclusiveAccessBubbleDisplayed();
}
void ExclusiveAccessTest::FinishExclusiveAccessBubbleAnimation() {
if (!GetExclusiveAccessBubbleView()) {
return;
}
gfx::AnimationTestApi animation_api(
GetExclusiveAccessBubbleView()->animation_for_test());
base::TimeTicks far_future = base::TimeTicks::Now() + base::Seconds(1);
animation_api.Step(far_future);
}
FullscreenController* ExclusiveAccessTest::GetFullscreenController() {
return GetExclusiveAccessManager()->fullscreen_controller();
}
ExclusiveAccessManager* ExclusiveAccessTest::GetExclusiveAccessManager() {
return browser()->GetFeatures().exclusive_access_manager();
}
void ExclusiveAccessTest::SetEscRepeatWindowLength(
base::TimeDelta esc_repeat_window) {
GetExclusiveAccessManager()->keyboard_lock_controller()->esc_repeat_window_ =
esc_repeat_window;
}
void ExclusiveAccessTest::SetEscRepeatThresholdReachedCallback(
base::OnceClosure callback) {
GetExclusiveAccessManager()
->keyboard_lock_controller()
->esc_repeat_triggered_for_test_ = std::move(callback);
}
void ExclusiveAccessTest::SetEscRepeatTestTickClock(
const base::TickClock* tick_clock_for_test) {
GetExclusiveAccessManager()
->keyboard_lock_controller()
->esc_repeat_tick_clock_ = tick_clock_for_test;
}
void ExclusiveAccessTest::OnBubbleHidden(
std::vector<ExclusiveAccessBubbleHideReason>* reason_recorder,
ExclusiveAccessBubbleHideReason reason) {
reason_recorder->push_back(reason);
}
void ExclusiveAccessTest::SetUserEscapeTimestampForTest(
const base::TimeTicks timestamp) {
GetExclusiveAccessManager()
->pointer_lock_controller()
->last_user_escape_time_ = timestamp;
}
void ExclusiveAccessTest::ExpectMockControllerReceivedEscape(int count) {
EXPECT_EQ(count, mock_controller()->escape_pressed_count());
mock_controller()->reset_escape_pressed_count();
}
void ExclusiveAccessTest::Wait(base::TimeDelta duration) {
base::RunLoop run_loop;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), duration);
run_loop.Run();
}