blob: 8eb57dee5d5dc6a961112f3bdf9cfce998e25b1e [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/test/ui_controls.h"
#include <utility>
#include "base/check.h"
#include "base/functional/callback_helpers.h"
#include "ui/android/window_android.h"
namespace ui_controls {
namespace {
bool g_ui_controls_enabled = false;
void CheckUIControlsEnabled() {
CHECK(g_ui_controls_enabled)
<< "In order to use ui_controls methods, you must be in a test "
"executable that enables UI Controls. Currently, this is "
"android_browsertests on Android platform.\n"
"This limitation prevents attempting to send input that might require "
"the test process to be active and focused in an environment where "
"the process is not guaranteed to be running exclusively, which can "
"lead to flaky tests.";
}
// Note: the order of the bool params are different from SendKeyPress family
// in order to make them aligned with other parts of UI flags, such as
// EventFlags and AcceleratorState.
bool SendKeyEventsInternal(gfx::NativeWindow window,
ui::KeyboardCode key,
int key_event_types,
bool shift,
bool control,
bool alt,
bool command,
base::OnceClosure task) {
if (!window->SendKeyEventsForTesting(key, key_event_types, shift, control,
alt, command)) {
return false;
}
if (task) {
std::move(task).Run();
}
return true;
}
} // namespace
void EnableUIControls() {
// This should be called at most once per process.
CHECK(!g_ui_controls_enabled);
g_ui_controls_enabled = true;
}
bool SendKeyPress(gfx::NativeWindow window,
ui::KeyboardCode key,
bool control,
bool shift,
bool alt,
bool command) {
CheckUIControlsEnabled();
return SendKeyEventsInternal(
window, key, (KeyEventType::kKeyPress | KeyEventType::kKeyRelease), shift,
control, alt, command, /*task=*/base::OnceClosure());
}
bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
ui::KeyboardCode key,
bool control,
bool shift,
bool alt,
bool command,
base::OnceClosure task,
KeyEventType wait_for) {
CheckUIControlsEnabled();
// This doesn't time out if `window` is deleted before the key release events
// are dispatched, so it's fine to ignore `wait_for` and always wait for key
// release events. Just CHECK here.
CHECK(wait_for == ui_controls::KeyEventType::kKeyPress ||
wait_for == ui_controls::KeyEventType::kKeyRelease);
return SendKeyEventsInternal(
window, key, (KeyEventType::kKeyPress | KeyEventType::kKeyRelease), shift,
control, alt, command, std::move(task));
}
bool SendKeyEvents(gfx::NativeWindow window,
ui::KeyboardCode key,
int key_event_types,
int accelerator_state) {
CheckUIControlsEnabled();
return SendKeyEventsInternal(
window, key, key_event_types,
/*shift=*/(accelerator_state & AcceleratorState::kShift),
/*control=*/(accelerator_state & AcceleratorState::kControl),
/*alt=*/(accelerator_state & AcceleratorState::kAlt),
/*command=*/(accelerator_state & AcceleratorState::kCommand),
/*task=*/base::NullCallback());
}
bool SendKeyEventsNotifyWhenDone(gfx::NativeWindow window,
ui::KeyboardCode key,
int key_event_types,
base::OnceClosure task,
int accelerator_state) {
CheckUIControlsEnabled();
return SendKeyEventsInternal(
window, key, key_event_types,
/*shift=*/(accelerator_state & AcceleratorState::kShift),
/*control=*/(accelerator_state & AcceleratorState::kControl),
/*alt=*/(accelerator_state & AcceleratorState::kAlt),
/*command=*/(accelerator_state & AcceleratorState::kCommand),
std::move(task));
}
} // namespace ui_controls