#include <optional>
#include <utility>
#include "ash/accelerators/accelerator_commands.h"
#include "ash/accelerators/accelerator_controller_impl.h"
#include "ash/accelerators/accelerator_history_impl.h"
#include "ash/accelerators/accelerator_notifications.h"
#include "ash/accelerators/accelerator_table.h"
#include "ash/accelerators/pre_target_accelerator_handler.h"
#include "ash/accelerators/system_shortcut_behavior_policy.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/accessibility/magnifier/docked_magnifier_controller.h"
#include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
#include "ash/accessibility/test_accessibility_controller_client.h"
#include "ash/accessibility/ui/accessibility_confirmation_dialog.h"
#include "ash/app_list/test/app_list_test_helper.h"
#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_types.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/display/display_configuration_controller_test_api.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/display/screen_orientation_controller_test_api.h"
#include "ash/frame/frame_view_ash.h"
#include "ash/game_dashboard/test_game_dashboard_delegate.h"
#include "ash/ime/ime_controller_impl.h"
#include "ash/ime/test_ime_controller_client.h"
#include "ash/media/media_controller_impl.h"
#include "ash/public/cpp/accelerator_actions.h"
#include "ash/public/cpp/accelerators.h"
#include "ash/public/cpp/arc_game_controls_flag.h"
#include "ash/public/cpp/ash_prefs.h"
#include "ash/public/cpp/capture_mode/capture_mode_test_api.h"
#include "ash/public/cpp/ime_info.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "ash/public/cpp/test/test_new_window_delegate.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/brightness_control_delegate.h"
#include "ash/system/keyboard_brightness_control_delegate.h"
#include "ash/system/power/power_button_controller_test_api.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "ash/system/toast/anchored_nudge.h"
#include "ash/system/toast/anchored_nudge_manager_impl.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/test/ash_test_base.h"
#include "ash/test_media_client.h"
#include "ash/wm/lock_state_controller.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_item.h"
#include "ash/wm/overview/overview_test_util.h"
#include "ash/wm/splitview/split_view_metrics_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
#include "ash/wm/test/test_session_state_animator.h"
#include "ash/wm/window_positioning_utils.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/json/json_writer.h"
#include "base/json/values_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/system/sys_info.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_chromeos_version_info.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/ui/base/window_state_type.h"
#include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
#include "chromeos/ui/frame/caption_buttons/frame_size_button.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/prefs/testing_pref_service.h"
#include "components/user_manager/user_type.h"
#include "media/base/media_switches.h"
#include "services/media_session/public/cpp/test/test_media_controller.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/test_windows.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/media_keys_util.h"
#include "ui/base/accelerators/test_accelerator_target.h"
#include "ui/base/ime/ash/fake_ime_keyboard.h"
#include "ui/base/ime/ash/mock_input_method_manager.h"
#include "ui/base/ime/init/input_method_factory.h"
#include "ui/base/ime/mock_input_method.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/events/ash/keyboard_capability.h"
#include "ui/events/devices/device_data_manager_test_api.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/keyboard_device.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_handler.h"
#include "ui/events/event_sink.h"
#include "ui/events/event_targeter.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/test/event_generator.h"
#include "ui/events/types/event_type.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notification_types.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/accelerator_filter.h"
namespace ash {
namespace {
using ::chromeos::WindowStateType;
using ::media_session::mojom::MediaSessionAction;
constexpr char kUserEmail[] = "user@testemail.com";
struct PrefToAcceleratorEntry {
const char* pref_name;
const char* notification_id;
const ui::Accelerator accelerator;
};
constexpr char kCapsLockNoMatchNudgeId[] = "caps-lock-no-match-nudge-id";
const PrefToAcceleratorEntry kAccessibilityAcceleratorMap[] = {
{
prefs::kAccessibilityHighContrastEnabled,
kHighContrastToggleAccelNotificationId,
ui::Accelerator(ui::VKEY_H, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN),
},
{prefs::kDockedMagnifierEnabled, kDockedMagnifierToggleAccelNotificationId,
ui::Accelerator(ui::VKEY_D, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN)},
{
prefs::kAccessibilitySpokenFeedbackEnabled,
nullptr,
ui::Accelerator(ui::VKEY_Z, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN),
},
{prefs::kAccessibilityScreenMagnifierEnabled,
kFullscreenMagnifierToggleAccelNotificationId,
ui::Accelerator(ui::VKEY_M, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN)},
};
void AddTestImes() {
ImeInfo ime1;
ime1.id = "id1";
ImeInfo ime2;
ime2.id = "id2";
std::vector<ImeInfo> visible_imes;
visible_imes.push_back(std::move(ime1));
visible_imes.push_back(std::move(ime2));
Shell::Get()->ime_controller()->RefreshIme("id1", std::move(visible_imes),
std::vector<ImeMenuItem>());
}
void AddNotVisibleTestIme() {
ImeInfo dictation;
dictation.id = "_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation";
const std::vector<ImeInfo> visible_imes =
Shell::Get()->ime_controller()->GetVisibleImes();
std::vector<ImeInfo> available_imes;
for (auto ime : visible_imes) {
available_imes.push_back(ime);
}
available_imes.push_back(dictation);
Shell::Get()->ime_controller()->RefreshIme(
dictation.id, std::move(available_imes), std::vector<ImeMenuItem>());
}
ui::Accelerator CreateReleaseAccelerator(ui::KeyboardCode key_code,
int modifiers) {
ui::Accelerator accelerator(key_code, modifiers);
accelerator.set_key_state(ui::Accelerator::KeyState::RELEASED);
return accelerator;
}
class DummyBrightnessControlDelegate : public BrightnessControlDelegate {
public:
DummyBrightnessControlDelegate()
: handle_brightness_down_count_(0), handle_brightness_up_count_(0) {}
DummyBrightnessControlDelegate(const DummyBrightnessControlDelegate&) =
delete;
DummyBrightnessControlDelegate& operator=(
const DummyBrightnessControlDelegate&) = delete;
~DummyBrightnessControlDelegate() override = default;
void HandleBrightnessDown() override {
++handle_brightness_down_count_;
last_accelerator_ = ui::Accelerator(ui::VKEY_BRIGHTNESS_DOWN, ui::EF_NONE);
}
void HandleBrightnessUp() override {
++handle_brightness_up_count_;
last_accelerator_ = ui::Accelerator(ui::VKEY_BRIGHTNESS_UP, ui::EF_NONE);
}
void SetBrightnessPercent(double percent,
bool gradual,
BrightnessChangeSource source) override {}
void GetBrightnessPercent(
base::OnceCallback<void(std::optional<double>)> callback) override {
std::move(callback).Run(100.0);
}
void SetAmbientLightSensorEnabled(
bool enabled,
AmbientLightSensorEnabledChangeSource source) override {}
void GetAmbientLightSensorEnabled(
base::OnceCallback<void(std::optional<bool>)> callback) override {
std::move(callback).Run(true);
}
void HasAmbientLightSensor(
base::OnceCallback<void(std::optional<bool>)> callback) override {
std::move(callback).Run(true);
}
int handle_brightness_down_count() const {
return handle_brightness_down_count_;
}
int handle_brightness_up_count() const { return handle_brightness_up_count_; }
const ui::Accelerator& last_accelerator() const { return last_accelerator_; }
private:
int handle_brightness_down_count_;
int handle_brightness_up_count_;
ui::Accelerator last_accelerator_;
};
class DummyKeyboardBrightnessControlDelegate
: public KeyboardBrightnessControlDelegate {
public:
DummyKeyboardBrightnessControlDelegate() = default;
DummyKeyboardBrightnessControlDelegate(
const DummyKeyboardBrightnessControlDelegate&) = delete;
DummyKeyboardBrightnessControlDelegate& operator=(
const DummyKeyboardBrightnessControlDelegate&) = delete;
~DummyKeyboardBrightnessControlDelegate() override = default;
void HandleKeyboardBrightnessDown() override {
++handle_keyboard_brightness_down_count_;
last_accelerator_ =
ui::Accelerator(ui::VKEY_BRIGHTNESS_DOWN, ui::EF_ALT_DOWN);
}
void HandleKeyboardBrightnessUp() override {
++handle_keyboard_brightness_up_count_;
last_accelerator_ =
ui::Accelerator(ui::VKEY_BRIGHTNESS_UP, ui::EF_ALT_DOWN);
}
void HandleToggleKeyboardBacklight() override {
++handle_toggle_keyboard_backlight_count_;
last_accelerator_ =
ui::Accelerator(ui::VKEY_KBD_BACKLIGHT_TOGGLE, ui::EF_NONE);
}
void HandleSetKeyboardBrightness(
double percent,
bool gradual,
KeyboardBrightnessChangeSource source) override {}
void HandleGetKeyboardBrightness(
base::OnceCallback<void(std::optional<double>)> callback) override {
std::move(callback).Run(100.0);
}
void HandleSetKeyboardAmbientLightSensorEnabled(
bool enabled,
KeyboardAmbientLightSensorEnabledChangeSource source) override {}
void HandleGetKeyboardAmbientLightSensorEnabled(
base::OnceCallback<void(std::optional<bool>)> callback) override {}
int handle_keyboard_brightness_down_count() const {
return handle_keyboard_brightness_down_count_;
}
int handle_keyboard_brightness_up_count() const {
return handle_keyboard_brightness_up_count_;
}
int handle_toggle_keyboard_backlight_count() const {
return handle_toggle_keyboard_backlight_count_;
}
const ui::Accelerator& last_accelerator() const { return last_accelerator_; }
private:
int handle_keyboard_brightness_down_count_ = 0;
int handle_keyboard_brightness_up_count_ = 0;
int handle_toggle_keyboard_backlight_count_ = 0;
ui::Accelerator last_accelerator_;
};
class MockNewWindowDelegate : public testing::NiceMock<TestNewWindowDelegate> {
public:
MOCK_METHOD(void, OpenCalculator, (), (override));
MOCK_METHOD(void,
OpenUrl,
(const GURL& url, OpenUrlFrom from, Disposition disposition),
(override));
};
class MockAcceleratorObserver
: public testing::NiceMock<AcceleratorController::Observer> {
public:
MOCK_METHOD(void, OnActionPerformed, (AcceleratorAction action), (override));
MOCK_METHOD(void,
OnAcceleratorControllerWillBeDestroyed,
(AcceleratorController * controller),
(override));
};
class VoidEventHandler : public ui::EventHandler {
public:
VoidEventHandler() = default;
void OnEvent(ui::Event* event) override {
num_events_received_++;
event->SetHandled();
event->StopPropagation();
}
void ResetEventCounter() { num_events_received_ = 0; }
int num_events_received() const { return num_events_received_; }
private:
int num_events_received_ = 0;
};
}
class AcceleratorControllerTest : public AshTestBase {
public:
AcceleratorControllerTest() = default;
AcceleratorControllerTest(const AcceleratorControllerTest&) = delete;
AcceleratorControllerTest& operator=(const AcceleratorControllerTest&) =
delete;
~AcceleratorControllerTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
controller_ = Shell::Get()->accelerator_controller();
test_api_ =
std::make_unique<AcceleratorControllerImpl::TestApi>(controller_);
}
void TearDown() override {
test_api_.reset();
controller_ = nullptr;
AshTestBase::TearDown();
}
protected:
static bool ProcessInController(const ui::Accelerator& accelerator) {
AcceleratorControllerImpl* controller =
Shell::Get()->accelerator_controller();
if (accelerator.key_state() == ui::Accelerator::KeyState::RELEASED) {
ui::Accelerator pressed_accelerator = accelerator;
pressed_accelerator.set_key_state(ui::Accelerator::KeyState::PRESSED);
controller->GetAcceleratorHistory()->StoreCurrentAccelerator(
pressed_accelerator);
}
controller->GetAcceleratorHistory()->StoreCurrentAccelerator(accelerator);
return controller->Process(accelerator);
}
bool ContainsHighContrastNotification() const {
return nullptr != message_center()->FindVisibleNotificationById(
kHighContrastToggleAccelNotificationId);
}
bool ContainsDockedMagnifierNotification() const {
return nullptr != message_center()->FindVisibleNotificationById(
kDockedMagnifierToggleAccelNotificationId);
}
bool ContainsFullscreenMagnifierNotification() const {
return nullptr != message_center()->FindVisibleNotificationById(
kFullscreenMagnifierToggleAccelNotificationId);
}
bool IsNotificationPinned(const std::string& id) const {
auto* notification = message_center()->FindVisibleNotificationById(id);
DCHECK(notification);
return notification->pinned() ||
notification->priority() == message_center::SYSTEM_PRIORITY;
}
AccessibilityConfirmationDialog* GetConfirmationDialog() {
return Shell::Get()
->accessibility_controller()
->GetConfirmationDialogForTest();
}
bool IsConfirmationDialogOpen() { return !!GetConfirmationDialog(); }
void AcceptConfirmationDialog() {
DCHECK(GetConfirmationDialog());
GetConfirmationDialog()->AcceptDialog();
}
void CancelConfirmationDialog() {
DCHECK(GetConfirmationDialog());
GetConfirmationDialog()->CancelDialog();
}
void TriggerRotateScreenShortcut() {
PressAndReleaseKey(ui::VKEY_BROWSER_REFRESH,
ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
if (IsConfirmationDialogOpen()) {
AcceptConfirmationDialog();
base::RunLoop().RunUntilIdle();
}
}
void RemoveAllNotifications() const {
message_center()->RemoveAllNotifications(
false , message_center::MessageCenter::RemoveType::ALL);
}
static const ui::Accelerator& GetPreviousAccelerator() {
return Shell::Get()
->accelerator_controller()
->GetAcceleratorHistory()
->previous_accelerator();
}
static const ui::Accelerator& GetCurrentAccelerator() {
return Shell::Get()
->accelerator_controller()
->GetAcceleratorHistory()
->current_accelerator();
}
static void StubForTest(ExitWarningHandler* ewh) {
ewh->stub_timer_for_test_ = true;
}
static void Reset(ExitWarningHandler* ewh) {
ewh->state_ = ExitWarningHandler::IDLE;
}
static void SimulateTimerExpired(ExitWarningHandler* ewh) {
ewh->TimerAction();
}
static bool is_ui_shown(ExitWarningHandler* ewh) { return !!ewh->widget_; }
static views::View* GetContentsView(ExitWarningHandler* ewh) {
return ewh->widget_->GetContentsView();
}
static bool is_idle(ExitWarningHandler* ewh) {
return ewh->state_ == ExitWarningHandler::IDLE;
}
static bool is_exiting(ExitWarningHandler* ewh) {
return ewh->state_ == ExitWarningHandler::EXITING;
}
message_center::MessageCenter* message_center() const {
return message_center::MessageCenter::Get();
}
void SetBrightnessControlDelegate(
std::unique_ptr<BrightnessControlDelegate> delegate) {
Shell::Get()->brightness_control_delegate_ = std::move(delegate);
}
void SetKeyboardBrightnessControlDelegate(
std::unique_ptr<KeyboardBrightnessControlDelegate> delegate) {
Shell::Get()->keyboard_brightness_control_delegate_ = std::move(delegate);
}
bool WriteJsonFile(const base::FilePath& file_path,
const std::string& json_string) const {
if (!base::DirectoryExists(file_path.DirName()))
base::CreateDirectory(file_path.DirName());
if (!base::WriteFile(file_path, json_string)) {
LOG(ERROR) << "Writing to " << file_path.value() << " failed.";
return false;
}
return true;
}
MockNewWindowDelegate& new_window_delegate() { return new_window_delegate_; }
raw_ptr<AcceleratorControllerImpl> controller_ = nullptr;
std::unique_ptr<AcceleratorControllerImpl::TestApi> test_api_;
private:
MockNewWindowDelegate new_window_delegate_;
};
namespace {
TEST_F(AcceleratorControllerTest, ExitWarningHandlerTestDoublePress) {
ui::Accelerator press(ui::VKEY_Q, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN);
ui::Accelerator release(press);
release.set_key_state(ui::Accelerator::KeyState::RELEASED);
ExitWarningHandler* ewh = test_api_->GetExitWarningHandler();
ASSERT_TRUE(ewh);
StubForTest(ewh);
EXPECT_TRUE(is_idle(ewh));
EXPECT_FALSE(is_ui_shown(ewh));
EXPECT_TRUE(ProcessInController(press));
EXPECT_FALSE(ProcessInController(release));
EXPECT_FALSE(is_idle(ewh));
EXPECT_TRUE(is_ui_shown(ewh));
EXPECT_TRUE(ProcessInController(press));
EXPECT_FALSE(ProcessInController(release));
SimulateTimerExpired(ewh);
EXPECT_TRUE(is_exiting(ewh));
EXPECT_FALSE(is_ui_shown(ewh));
Reset(ewh);
}
TEST_F(AcceleratorControllerTest, ExitWarningHandlerTestSinglePress) {
ui::Accelerator press(ui::VKEY_Q, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN);
ui::Accelerator release(press);
release.set_key_state(ui::Accelerator::KeyState::RELEASED);
ExitWarningHandler* ewh = test_api_->GetExitWarningHandler();
ASSERT_TRUE(ewh);
StubForTest(ewh);
EXPECT_TRUE(is_idle(ewh));
EXPECT_FALSE(is_ui_shown(ewh));
EXPECT_TRUE(ProcessInController(press));
EXPECT_FALSE(ProcessInController(release));
EXPECT_FALSE(is_idle(ewh));
EXPECT_TRUE(is_ui_shown(ewh));
SimulateTimerExpired(ewh);
EXPECT_TRUE(is_idle(ewh));
EXPECT_FALSE(is_ui_shown(ewh));
Reset(ewh);
}
TEST_F(AcceleratorControllerTest, LingeringExitWarningBubble) {
ExitWarningHandler* ewh = test_api_->GetExitWarningHandler();
ASSERT_TRUE(ewh);
StubForTest(ewh);
ewh->HandleAccelerator();
EXPECT_FALSE(is_idle(ewh));
EXPECT_TRUE(is_ui_shown(ewh));
}
TEST_F(AcceleratorControllerTest,
ExitWarningWidgetDelegateViewAccessibleProperties) {
ExitWarningHandler* ewh = test_api_->GetExitWarningHandler();
ewh->HandleAccelerator();
auto* delegate_view = GetContentsView(ewh);
ui::AXNodeData data;
delegate_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_EQ(data.role, ax::mojom::Role::kAlert);
EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
l10n_util::GetStringUTF16(
IDS_ASH_SIGN_OUT_WARNING_POPUP_TEXT_ACCESSIBLE));
}
TEST_F(AcceleratorControllerTest, Register) {
ui::TestAcceleratorTarget target;
const ui::Accelerator accelerator_a(ui::VKEY_A, ui::EF_NONE);
const ui::Accelerator accelerator_b(ui::VKEY_B, ui::EF_NONE);
const ui::Accelerator accelerator_c(ui::VKEY_C, ui::EF_NONE);
const ui::Accelerator accelerator_d(ui::VKEY_D, ui::EF_NONE);
controller_->Register(
{accelerator_a, accelerator_b, accelerator_c, accelerator_d}, &target);
EXPECT_TRUE(ProcessInController(accelerator_a));
EXPECT_TRUE(ProcessInController(accelerator_b));
EXPECT_TRUE(ProcessInController(accelerator_c));
EXPECT_TRUE(ProcessInController(accelerator_d));
EXPECT_EQ(4, target.accelerator_count());
}
TEST_F(AcceleratorControllerTest, RegisterMultipleTarget) {
const ui::Accelerator accelerator_a(ui::VKEY_A, ui::EF_NONE);
ui::TestAcceleratorTarget target1;
controller_->Register({accelerator_a}, &target1);
ui::TestAcceleratorTarget target2;
controller_->Register({accelerator_a}, &target2);
EXPECT_TRUE(ProcessInController(accelerator_a));
EXPECT_EQ(0, target1.accelerator_count());
EXPECT_EQ(1, target2.accelerator_count());
}
TEST_F(AcceleratorControllerTest, Unregister) {
const ui::Accelerator accelerator_a(ui::VKEY_A, ui::EF_NONE);
const ui::Accelerator accelerator_b(ui::VKEY_B, ui::EF_NONE);
ui::TestAcceleratorTarget target;
controller_->Register({accelerator_a, accelerator_b}, &target);
controller_->Unregister(accelerator_b, &target);
EXPECT_TRUE(ProcessInController(accelerator_a));
EXPECT_EQ(1, target.accelerator_count());
target.ResetCounts();
controller_->Unregister(accelerator_a, &target);
EXPECT_FALSE(ProcessInController(accelerator_a));
EXPECT_EQ(0, target.accelerator_count());
}
TEST_F(AcceleratorControllerTest, UnregisterAll) {
const ui::Accelerator accelerator_a(ui::VKEY_A, ui::EF_NONE);
const ui::Accelerator accelerator_b(ui::VKEY_B, ui::EF_NONE);
ui::TestAcceleratorTarget target1;
controller_->Register({accelerator_a, accelerator_b}, &target1);
const ui::Accelerator accelerator_c(ui::VKEY_C, ui::EF_NONE);
ui::TestAcceleratorTarget target2;
controller_->Register({accelerator_c}, &target2);
controller_->UnregisterAll(&target1);
EXPECT_FALSE(ProcessInController(accelerator_a));
EXPECT_FALSE(ProcessInController(accelerator_b));
EXPECT_EQ(0, target1.accelerator_count());
EXPECT_TRUE(ProcessInController(accelerator_c));
EXPECT_EQ(1, target2.accelerator_count());
}
TEST_F(AcceleratorControllerTest, Process) {
const ui::Accelerator accelerator_a(ui::VKEY_A, ui::EF_NONE);
ui::TestAcceleratorTarget target1;
controller_->Register({accelerator_a}, &target1);
EXPECT_TRUE(ProcessInController(accelerator_a));
EXPECT_EQ(1, target1.accelerator_count());
const ui::Accelerator accelerator_b(ui::VKEY_B, ui::EF_NONE);
EXPECT_FALSE(ProcessInController(accelerator_b));
}
TEST_F(AcceleratorControllerTest, IsRegistered) {
const ui::Accelerator accelerator_a(ui::VKEY_A, ui::EF_NONE);
const ui::Accelerator accelerator_shift_a(ui::VKEY_A, ui::EF_SHIFT_DOWN);
ui::TestAcceleratorTarget target;
controller_->Register({accelerator_a}, &target);
EXPECT_TRUE(controller_->IsRegistered(accelerator_a));
EXPECT_FALSE(controller_->IsRegistered(accelerator_shift_a));
controller_->UnregisterAll(&target);
EXPECT_FALSE(controller_->IsRegistered(accelerator_a));
}
TEST_F(AcceleratorControllerTest, WindowSnap) {
std::unique_ptr<aura::Window> window(
CreateTestWindowInShell({.bounds = {5, 5, 20, 20}, .window_id = 0}));
WindowState* window_state = WindowState::Get(window.get());
window_state->Activate();
{
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
gfx::Rect expected_bounds = GetDefaultSnappedWindowBoundsInParent(
window.get(), SnapViewType::kPrimary);
EXPECT_EQ(expected_bounds.ToString(), window->bounds().ToString());
}
{
controller_->PerformActionIfEnabled(
AcceleratorAction::kWindowCycleSnapRight, {});
gfx::Rect expected_bounds = GetDefaultSnappedWindowBoundsInParent(
window.get(), SnapViewType::kSecondary);
EXPECT_EQ(expected_bounds.ToString(), window->bounds().ToString());
}
{
gfx::Rect normal_bounds = window_state->GetRestoreBoundsInParent();
controller_->PerformActionIfEnabled(AcceleratorAction::kToggleMaximized,
{});
EXPECT_TRUE(window_state->IsMaximized());
EXPECT_NE(normal_bounds.ToString(), window->bounds().ToString());
controller_->PerformActionIfEnabled(AcceleratorAction::kToggleMaximized,
{});
EXPECT_FALSE(window_state->IsMaximized());
gfx::Rect expected_bounds = GetDefaultSnappedWindowBoundsInParent(
window.get(), SnapViewType::kSecondary);
EXPECT_EQ(expected_bounds.ToString(), window->bounds().ToString());
EXPECT_EQ(window_state->GetStateType(), WindowStateType::kSecondarySnapped);
controller_->PerformActionIfEnabled(AcceleratorAction::kToggleMaximized,
{});
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
EXPECT_FALSE(window_state->IsMaximized());
controller_->PerformActionIfEnabled(AcceleratorAction::kToggleMaximized,
{});
controller_->PerformActionIfEnabled(
AcceleratorAction::kWindowCycleSnapRight, {});
EXPECT_FALSE(window_state->IsMaximized());
controller_->PerformActionIfEnabled(AcceleratorAction::kToggleMaximized,
{});
EXPECT_TRUE(window_state->IsMaximized());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowMinimize, {});
EXPECT_FALSE(window_state->IsMaximized());
EXPECT_TRUE(window_state->IsMinimized());
window_state->Restore();
window_state->Activate();
controller_->PerformActionIfEnabled(AcceleratorAction::kToggleFullscreen,
{});
EXPECT_TRUE(window_state->IsFullscreen());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
EXPECT_TRUE(window_state->IsSnapped());
EXPECT_FALSE(window_state->IsFullscreen());
controller_->PerformActionIfEnabled(AcceleratorAction::kToggleFullscreen,
{});
controller_->PerformActionIfEnabled(
AcceleratorAction::kWindowCycleSnapRight, {});
EXPECT_TRUE(window_state->IsSnapped());
EXPECT_FALSE(window_state->IsFullscreen());
}
{
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowMinimize, {});
EXPECT_TRUE(window_state->IsMinimized());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
EXPECT_TRUE(window_state->IsMinimized());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowMinimize, {});
EXPECT_FALSE(window_state->IsMinimized());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
EXPECT_TRUE(window_state->IsSnapped());
}
}
TEST_F(AcceleratorControllerTest, WindowSnapUpsideDown) {
const int64_t primary_display_id =
WindowTreeHostManager::GetPrimaryDisplayId();
DisplayConfigurationController* controller =
Shell::Get()->display_configuration_controller();
controller->SetDisplayRotation(
primary_display_id, display::Display::ROTATE_180,
display::Display::RotationSource::USER,
DisplayConfigurationController::ANIMATION_SYNC);
display::Display current_display =
display_manager()->GetDisplayForId(primary_display_id);
ASSERT_TRUE(current_display.is_landscape());
ASSERT_FALSE(chromeos::IsDisplayLayoutPrimary(current_display));
auto window = CreateAppWindow(gfx::Rect(300, 300));
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
EXPECT_EQ(gfx::Point(0, 0), window->GetBoundsInScreen().origin());
gfx::Rect work_area_bounds = current_display.work_area();
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapRight,
{});
EXPECT_EQ(work_area_bounds.top_right(),
window->GetBoundsInScreen().top_right());
controller->SetDisplayRotation(
primary_display_id, display::Display::ROTATE_90,
display::Display::RotationSource::USER,
DisplayConfigurationController::ANIMATION_SYNC);
current_display = display_manager()->GetDisplayForId(primary_display_id);
ASSERT_FALSE(current_display.is_landscape());
ASSERT_FALSE(chromeos::IsDisplayLayoutPrimary(current_display));
window = CreateAppWindow(gfx::Rect(300, 300));
work_area_bounds = current_display.work_area();
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
EXPECT_EQ(gfx::Point(0, 0), window->GetBoundsInScreen().origin());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapRight,
{});
EXPECT_EQ(work_area_bounds.bottom_left(),
window->GetBoundsInScreen().bottom_left());
}
TEST_F(AcceleratorControllerTest, TestRepeatedSnap) {
std::unique_ptr<aura::Window> window(
CreateTestWindowInShell({.bounds = {5, 5, 20, 20}, .window_id = 0}));
WindowState* window_state = WindowState::Get(window.get());
window_state->Activate();
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapRight,
{});
gfx::Rect normal_bounds = window_state->GetRestoreBoundsInParent();
gfx::Rect expected_bounds = GetDefaultSnappedWindowBoundsInParent(
window.get(), SnapViewType::kSecondary);
EXPECT_EQ(expected_bounds.ToString(), window->bounds().ToString());
EXPECT_TRUE(window_state->IsSnapped());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapRight,
{});
EXPECT_TRUE(window_state->IsNormalStateType());
EXPECT_EQ(normal_bounds.ToString(), window->bounds().ToString());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapRight,
{});
EXPECT_TRUE(window_state->IsSnapped());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
EXPECT_TRUE(window_state->IsSnapped());
expected_bounds = GetDefaultSnappedWindowBoundsInParent(
window.get(), SnapViewType::kPrimary);
EXPECT_EQ(expected_bounds.ToString(), window->bounds().ToString());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
EXPECT_TRUE(window_state->IsNormalStateType());
EXPECT_EQ(normal_bounds.ToString(), window->bounds().ToString());
}
class AcceleratorControllerTestWithClamshellSplitView
: public AcceleratorControllerTest {
public:
AcceleratorControllerTestWithClamshellSplitView() = default;
AcceleratorControllerTestWithClamshellSplitView(
const AcceleratorControllerTestWithClamshellSplitView&) = delete;
AcceleratorControllerTestWithClamshellSplitView& operator=(
const AcceleratorControllerTestWithClamshellSplitView&) = delete;
~AcceleratorControllerTestWithClamshellSplitView() override = default;
protected:
void EnterOverviewAndDragToSnapLeft(aura::Window* window) {
EnterOverviewAndDragTo(window, gfx::Point(0, 300));
}
void EnterOverviewAndDragToSnapRight(aura::Window* window) {
EnterOverviewAndDragTo(window, gfx::Point(799, 300));
}
private:
void EnterOverviewAndDragTo(aura::Window* window,
const gfx::Point& destination) {
CHECK(!OverviewController::Get()->InOverviewSession());
ToggleOverview();
ui::test::EventGenerator* generator = GetEventGenerator();
generator->MoveMouseTo(gfx::ToRoundedPoint(
GetOverviewItemForWindow(window)->target_bounds().CenterPoint()));
generator->DragMouseTo(destination);
}
};
TEST_F(AcceleratorControllerTestWithClamshellSplitView, WindowSnapUma) {
base::UserActionTester user_action_tester;
base::HistogramTester histogram_tester;
std::unique_ptr<aura::Window> window1(
CreateTestWindowInShell({.bounds = {10, 10, 20, 20}, .window_id = 0}));
std::unique_ptr<aura::Window> window2(
CreateTestWindowInShell({.bounds = {5, 5, 20, 20}, .window_id = 0}));
base::HistogramBase::Count32 left_clamshell_no_overview = 0;
base::HistogramBase::Count32 left_clamshell_overview = 0;
base::HistogramBase::Count32 left_tablet = 0;
base::HistogramBase::Count32 right_clamshell_no_overview = 0;
base::HistogramBase::Count32 right_clamshell_overview = 0;
base::HistogramBase::Count32 right_tablet = 0;
const auto test = [&](const char* description, AcceleratorAction action,
WindowStateType target_window1_state_type) {
SCOPED_TRACE(description);
controller_->PerformActionIfEnabled(action, {});
EXPECT_EQ(target_window1_state_type,
WindowState::Get(window1.get())->GetStateType());
EXPECT_EQ(
left_clamshell_no_overview + left_clamshell_overview + left_tablet,
user_action_tester.GetActionCount("Accel_Window_Snap_Left"));
histogram_tester.ExpectTotalCount(
"Ash.Accelerators.Actions.WindowCycleSnapLeft",
left_clamshell_no_overview + left_clamshell_overview + left_tablet);
EXPECT_EQ(
right_clamshell_no_overview + right_clamshell_overview + right_tablet,
user_action_tester.GetActionCount("Accel_Window_Snap_Right"));
histogram_tester.ExpectBucketCount(
kAccelWindowSnap,
WindowSnapAcceleratorAction::kCycleLeftSnapInClamshellNoOverview,
left_clamshell_no_overview);
histogram_tester.ExpectBucketCount(
kAccelWindowSnap,
WindowSnapAcceleratorAction::kCycleLeftSnapInClamshellOverview,
left_clamshell_overview);
histogram_tester.ExpectBucketCount(
kAccelWindowSnap, WindowSnapAcceleratorAction::kCycleLeftSnapInTablet,
left_tablet);
histogram_tester.ExpectBucketCount(
kAccelWindowSnap,
WindowSnapAcceleratorAction::kCycleRightSnapInClamshellNoOverview,
right_clamshell_no_overview);
histogram_tester.ExpectBucketCount(
kAccelWindowSnap,
WindowSnapAcceleratorAction::kCycleRightSnapInClamshellOverview,
right_clamshell_overview);
histogram_tester.ExpectBucketCount(
kAccelWindowSnap, WindowSnapAcceleratorAction::kCycleRightSnapInTablet,
right_tablet);
};
wm::ActivateWindow(window1.get());
left_clamshell_no_overview = 1;
test("Snap left, clamshell, no overview",
AcceleratorAction::kWindowCycleSnapLeft,
WindowStateType::kPrimarySnapped);
left_clamshell_no_overview = 2;
test("Unsnap left, clamshell, no overview",
AcceleratorAction::kWindowCycleSnapLeft, WindowStateType::kNormal);
EnterOverviewAndDragToSnapRight(window1.get());
left_clamshell_overview = 1;
test("Snap left, clamshell, overview",
AcceleratorAction::kWindowCycleSnapLeft,
WindowStateType::kPrimarySnapped);
left_clamshell_overview = 2;
test("Unsnap left, clamshell, overview",
AcceleratorAction::kWindowCycleSnapLeft, WindowStateType::kNormal);
right_clamshell_no_overview = 1;
test("Snap right, clamshell, no overview",
AcceleratorAction::kWindowCycleSnapRight,
WindowStateType::kSecondarySnapped);
right_clamshell_no_overview = 2;
test("Unsnap right, clamshell, no overview",
AcceleratorAction::kWindowCycleSnapRight, WindowStateType::kNormal);
EnterOverviewAndDragToSnapLeft(window1.get());
right_clamshell_overview = 1;
test("Snap right, clamshell, overview",
AcceleratorAction::kWindowCycleSnapRight,
WindowStateType::kSecondarySnapped);
right_clamshell_overview = 2;
test("Unsnap right, clamshell, overview",
AcceleratorAction::kWindowCycleSnapRight, WindowStateType::kNormal);
ShellTestApi().SetTabletModeEnabledForTest(true);
left_tablet = 1;
test("Snap left, tablet, no overview",
AcceleratorAction::kWindowCycleSnapLeft,
WindowStateType::kPrimarySnapped);
ToggleOverview();
left_tablet = 2;
test("Unsnap left, tablet, no overview",
AcceleratorAction::kWindowCycleSnapLeft, WindowStateType::kMaximized);
EnterOverviewAndDragToSnapRight(window1.get());
left_tablet = 3;
test("Snap left, tablet, overview", AcceleratorAction::kWindowCycleSnapLeft,
WindowStateType::kPrimarySnapped);
left_tablet = 4;
test("Unsnap left, tablet, overview", AcceleratorAction::kWindowCycleSnapLeft,
WindowStateType::kMaximized);
right_tablet = 1;
test("Snap right, tablet, no overview",
AcceleratorAction::kWindowCycleSnapRight,
WindowStateType::kSecondarySnapped);
ToggleOverview();
right_tablet = 2;
test("Unsnap right, tablet, no overview",
AcceleratorAction::kWindowCycleSnapRight, WindowStateType::kMaximized);
EnterOverviewAndDragToSnapLeft(window1.get());
right_tablet = 3;
test("Snap right, tablet, overview", AcceleratorAction::kWindowCycleSnapRight,
WindowStateType::kSecondarySnapped);
right_tablet = 4;
test("Unsnap right, tablet, overview",
AcceleratorAction::kWindowCycleSnapRight, WindowStateType::kMaximized);
}
TEST_F(AcceleratorControllerTestWithClamshellSplitView,
WindowSnapOrientationUma) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> window(
CreateTestWindowInShell({.bounds = {5, 5, 20, 20}, .window_id = 0}));
WindowState* window_state = WindowState::Get(window.get());
std::unique_ptr<aura::Window> window2(
CreateTestWindowInShell({.bounds = {5, 5, 20, 20}, .window_id = 0}));
WindowState* window_state2 = WindowState::Get(window2.get());
base::HistogramTester histogram_tester;
constexpr char kSnapWindowDeviceOrientationHistogram[] =
"Ash.Window.Snap.DeviceOrientation";
histogram_tester.ExpectBucketCount(
kSnapWindowDeviceOrientationHistogram,
SplitViewMetricsController::DeviceOrientation::kLandscape, 0);
window_state->Activate();
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
gfx::Rect expected_bounds = GetDefaultSnappedWindowBoundsInParent(
window.get(), SnapViewType::kPrimary);
EXPECT_EQ(expected_bounds.ToString(), window->bounds().ToString());
histogram_tester.ExpectBucketCount(
kSnapWindowDeviceOrientationHistogram,
SplitViewMetricsController::DeviceOrientation::kLandscape, 1);
histogram_tester.ExpectBucketCount(
kSnapWindowDeviceOrientationHistogram,
SplitViewMetricsController::DeviceOrientation::kPortrait, 0);
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapRight,
{});
histogram_tester.ExpectBucketCount(
kSnapWindowDeviceOrientationHistogram,
SplitViewMetricsController::DeviceOrientation::kLandscape, 2);
histogram_tester.ExpectBucketCount(
kSnapWindowDeviceOrientationHistogram,
SplitViewMetricsController::DeviceOrientation::kPortrait, 0);
window_state2->Activate();
UpdateDisplay("800x600/l");
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowCycleSnapLeft,
{});
histogram_tester.ExpectBucketCount(
kSnapWindowDeviceOrientationHistogram,
SplitViewMetricsController::DeviceOrientation::kPortrait, 1);
}
TEST_F(AcceleratorControllerTest, RotateScreen) {
display::Display display = display::Screen::Get()->GetPrimaryDisplay();
display::Display::Rotation initial_rotation =
GetActiveDisplayRotation(display.id());
AccessibilityController* accessibility_controller =
Shell::Get()->accessibility_controller();
EXPECT_FALSE(accessibility_controller
->HasDisplayRotationAcceleratorDialogBeenAccepted());
PressAndReleaseKey(ui::VKEY_BROWSER_REFRESH,
ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_TRUE(IsConfirmationDialogOpen());
CancelConfirmationDialog();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(accessibility_controller
->HasDisplayRotationAcceleratorDialogBeenAccepted());
display::Display::Rotation rotation_after_cancel =
GetActiveDisplayRotation(display.id());
EXPECT_EQ(initial_rotation, rotation_after_cancel);
PressAndReleaseKey(ui::VKEY_BROWSER_REFRESH,
ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_TRUE(IsConfirmationDialogOpen());
AcceptConfirmationDialog();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(IsConfirmationDialogOpen());
EXPECT_TRUE(accessibility_controller
->HasDisplayRotationAcceleratorDialogBeenAccepted());
display::Display::Rotation rotation_after_accept =
GetActiveDisplayRotation(display.id());
EXPECT_NE(initial_rotation, rotation_after_accept);
}
TEST_F(AcceleratorControllerTest, RotateScreenInPhysicalTabletState) {
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
ShellTestApi().SetTabletModeEnabledForTest(true);
auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
auto* screen_orientation_controller =
Shell::Get()->screen_orientation_controller();
EXPECT_TRUE(tablet_mode_controller->is_in_tablet_physical_state());
EXPECT_FALSE(screen_orientation_controller->user_rotation_locked());
EXPECT_FALSE(screen_orientation_controller->rotation_locked());
EXPECT_EQ(chromeos::OrientationType::kLandscapePrimary,
screen_orientation_controller->GetCurrentOrientation());
TriggerRotateScreenShortcut();
EXPECT_TRUE(screen_orientation_controller->user_rotation_locked());
EXPECT_TRUE(screen_orientation_controller->rotation_locked());
EXPECT_EQ(chromeos::OrientationType::kPortraitSecondary,
screen_orientation_controller->GetCurrentOrientation());
ShellTestApi().SetTabletModeEnabledForTest(false);
EXPECT_FALSE(tablet_mode_controller->is_in_tablet_physical_state());
EXPECT_EQ(chromeos::OrientationType::kLandscapePrimary,
screen_orientation_controller->GetCurrentOrientation());
EXPECT_TRUE(screen_orientation_controller->user_rotation_locked());
EXPECT_FALSE(screen_orientation_controller->rotation_locked());
}
TEST_F(AcceleratorControllerTest,
RotateScreenWithClamshellAutoRotationSupported) {
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kSupportsClamshellAutoRotation);
auto* screen_orientation_controller =
Shell::Get()->screen_orientation_controller();
EXPECT_TRUE(screen_orientation_controller->IsAutoRotationAllowed());
EXPECT_FALSE(screen_orientation_controller->user_rotation_locked());
EXPECT_FALSE(screen_orientation_controller->rotation_locked());
EXPECT_EQ(chromeos::OrientationType::kLandscapePrimary,
screen_orientation_controller->GetCurrentOrientation());
TriggerRotateScreenShortcut();
EXPECT_TRUE(screen_orientation_controller->user_rotation_locked());
EXPECT_TRUE(screen_orientation_controller->rotation_locked());
EXPECT_EQ(chromeos::OrientationType::kPortraitSecondary,
screen_orientation_controller->GetCurrentOrientation());
}
TEST_F(AcceleratorControllerTest, RotateScreenWithWindowLockingOrientation) {
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
ShellTestApi().SetTabletModeEnabledForTest(true);
auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
auto* screen_orientation_controller =
Shell::Get()->screen_orientation_controller();
EXPECT_TRUE(tablet_mode_controller->is_in_tablet_physical_state());
EXPECT_FALSE(screen_orientation_controller->user_rotation_locked());
auto win0 = CreateAppWindow(gfx::Rect{100, 300});
auto win1 = CreateAppWindow(gfx::Rect{200, 200});
screen_orientation_controller->LockOrientationForWindow(
win0.get(), chromeos::OrientationType::kPortraitPrimary);
screen_orientation_controller->LockOrientationForWindow(
win1.get(), chromeos::OrientationType::kLandscape);
wm::ActivateWindow(win0.get());
EXPECT_TRUE(screen_orientation_controller->rotation_locked());
EXPECT_FALSE(screen_orientation_controller->user_rotation_locked());
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
screen_orientation_controller->GetCurrentOrientation());
TriggerRotateScreenShortcut();
EXPECT_TRUE(screen_orientation_controller->rotation_locked());
EXPECT_FALSE(screen_orientation_controller->user_rotation_locked());
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
screen_orientation_controller->GetCurrentOrientation());
wm::ActivateWindow(win1.get());
EXPECT_TRUE(screen_orientation_controller->rotation_locked());
EXPECT_FALSE(screen_orientation_controller->user_rotation_locked());
EXPECT_EQ(chromeos::OrientationType::kLandscapePrimary,
screen_orientation_controller->GetCurrentOrientation());
TriggerRotateScreenShortcut();
EXPECT_TRUE(screen_orientation_controller->rotation_locked());
EXPECT_TRUE(screen_orientation_controller->user_rotation_locked());
EXPECT_EQ(chromeos::OrientationType::kLandscapeSecondary,
screen_orientation_controller->GetCurrentOrientation());
TriggerRotateScreenShortcut();
EXPECT_TRUE(screen_orientation_controller->rotation_locked());
EXPECT_TRUE(screen_orientation_controller->user_rotation_locked());
EXPECT_EQ(chromeos::OrientationType::kLandscapePrimary,
screen_orientation_controller->GetCurrentOrientation());
TabletModeControllerTestApi().AttachExternalMouse();
EXPECT_TRUE(tablet_mode_controller->is_in_tablet_physical_state());
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
wm::ActivateWindow(win0.get());
EXPECT_TRUE(screen_orientation_controller->rotation_locked());
EXPECT_TRUE(screen_orientation_controller->user_rotation_locked());
EXPECT_EQ(chromeos::OrientationType::kLandscapePrimary,
screen_orientation_controller->GetCurrentOrientation());
TriggerRotateScreenShortcut();
EXPECT_EQ(chromeos::OrientationType::kPortraitSecondary,
screen_orientation_controller->GetCurrentOrientation());
TriggerRotateScreenShortcut();
EXPECT_EQ(chromeos::OrientationType::kLandscapeSecondary,
screen_orientation_controller->GetCurrentOrientation());
wm::ActivateWindow(win1.get());
TriggerRotateScreenShortcut();
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
screen_orientation_controller->GetCurrentOrientation());
TriggerRotateScreenShortcut();
EXPECT_EQ(chromeos::OrientationType::kLandscapePrimary,
screen_orientation_controller->GetCurrentOrientation());
}
TEST_F(AcceleratorControllerTest, AutoRepeat) {
ui::Accelerator accelerator_a(ui::VKEY_A, ui::EF_CONTROL_DOWN);
ui::TestAcceleratorTarget target_a;
controller_->Register({accelerator_a}, &target_a);
ui::Accelerator accelerator_b(ui::VKEY_B, ui::EF_CONTROL_DOWN);
ui::TestAcceleratorTarget target_b;
controller_->Register({accelerator_b}, &target_b);
ui::test::EventGenerator* generator = GetEventGenerator();
generator->PressKey(ui::VKEY_A, ui::EF_CONTROL_DOWN);
generator->ReleaseKey(ui::VKEY_A, ui::EF_CONTROL_DOWN);
EXPECT_EQ(1, target_a.accelerator_count());
EXPECT_EQ(0, target_a.accelerator_repeat_count());
generator->PressKey(ui::VKEY_A, ui::EF_CONTROL_DOWN);
generator->PressKey(ui::VKEY_A, ui::EF_CONTROL_DOWN | ui::EF_IS_REPEAT);
EXPECT_EQ(2, target_a.accelerator_non_repeat_count());
EXPECT_EQ(1, target_a.accelerator_repeat_count());
generator->PressKey(ui::VKEY_A, ui::EF_CONTROL_DOWN | ui::EF_IS_REPEAT);
EXPECT_EQ(2, target_a.accelerator_non_repeat_count());
EXPECT_EQ(2, target_a.accelerator_repeat_count());
generator->ReleaseKey(ui::VKEY_A, ui::EF_CONTROL_DOWN);
EXPECT_EQ(2, target_a.accelerator_non_repeat_count());
EXPECT_EQ(2, target_a.accelerator_repeat_count());
generator->PressKey(ui::VKEY_A, ui::EF_CONTROL_DOWN);
generator->PressKey(ui::VKEY_A, ui::EF_CONTROL_DOWN | ui::EF_IS_REPEAT);
generator->PressKey(ui::VKEY_B, ui::EF_CONTROL_DOWN);
generator->ReleaseKey(ui::VKEY_B, ui::EF_CONTROL_DOWN);
generator->PressKey(ui::VKEY_A, ui::EF_CONTROL_DOWN);
generator->PressKey(ui::VKEY_A, ui::EF_CONTROL_DOWN | ui::EF_IS_REPEAT);
generator->ReleaseKey(ui::VKEY_A, ui::EF_CONTROL_DOWN);
EXPECT_EQ(1, target_b.accelerator_non_repeat_count());
EXPECT_EQ(0, target_b.accelerator_repeat_count());
EXPECT_EQ(4, target_a.accelerator_non_repeat_count());
EXPECT_EQ(4, target_a.accelerator_repeat_count());
}
TEST_F(AcceleratorControllerTest, Previous) {
PressAndReleaseKey(ui::VKEY_VOLUME_MUTE, ui::EF_NONE);
EXPECT_EQ(ui::VKEY_VOLUME_MUTE, GetPreviousAccelerator().key_code());
EXPECT_EQ(ui::EF_NONE, GetPreviousAccelerator().modifiers());
PressAndReleaseKey(ui::VKEY_TAB, ui::EF_CONTROL_DOWN);
EXPECT_EQ(ui::VKEY_TAB, GetPreviousAccelerator().key_code());
EXPECT_EQ(ui::EF_CONTROL_DOWN, GetPreviousAccelerator().modifiers());
}
TEST_F(AcceleratorControllerTest, DontRepeatToggleFullscreen) {
const AcceleratorData accelerators[] = {
{true, ui::VKEY_J, ui::EF_ALT_DOWN, AcceleratorAction::kToggleFullscreen},
{true, ui::VKEY_K, ui::EF_ALT_DOWN, AcceleratorAction::kToggleFullscreen},
};
test_api_->RegisterAccelerators(accelerators);
views::Widget::InitParams params(
views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET,
views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(5, 5, 20, 20);
views::Widget* widget = new views::Widget;
params.context = GetContext();
widget->Init(std::move(params));
widget->Show();
widget->Activate();
widget->GetNativeView()->SetProperty(
aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorCanMaximize |
aura::client::kResizeBehaviorCanFullscreen);
ui::test::EventGenerator* generator = GetEventGenerator();
WindowState* window_state = WindowState::Get(widget->GetNativeView());
generator->PressKey(ui::VKEY_J, ui::EF_ALT_DOWN);
EXPECT_TRUE(window_state->IsFullscreen());
generator->PressKey(ui::VKEY_J, ui::EF_ALT_DOWN | ui::EF_IS_REPEAT);
EXPECT_TRUE(window_state->IsFullscreen());
generator->PressKey(ui::VKEY_K, ui::EF_ALT_DOWN);
EXPECT_FALSE(window_state->IsFullscreen());
}
TEST_F(AcceleratorControllerTest, DontToggleFullscreenWhenOverviewStarts) {
std::unique_ptr<views::Widget> widget(CreateTestWidget(
views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, nullptr,
desks_util::GetActiveDeskContainerId(), gfx::Rect(400, 400)));
ui::test::EventGenerator* generator = GetEventGenerator();
generator->PressKey(ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_NONE);
generator->PressKey(ui::VKEY_ZOOM, ui::EF_NONE);
EXPECT_FALSE(WindowState::Get(widget->GetNativeWindow())->IsFullscreen());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_TRUE(OverviewController::Get()->overview_session()->IsWindowInOverview(
widget->GetNativeWindow()));
}
TEST_F(AcceleratorControllerTest, MinimizedWindowInOverview) {
std::unique_ptr<aura::Window> window(
CreateTestWindowInShell({.bounds = {5, 5, 20, 20}, .window_id = 0}));
WindowState* window_state = WindowState::Get(window.get());
window_state->Minimize();
EXPECT_TRUE(window_state->IsMinimized());
ToggleOverview();
GetEventGenerator()->PressKey(ui::VKEY_OEM_4, ui::EF_ALT_DOWN);
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_TRUE(window_state->IsMinimized());
}
TEST_F(AcceleratorControllerTest, ProcessOnce) {
DisableIME();
ui::Accelerator accelerator_a(ui::VKEY_A, ui::EF_NONE);
ui::TestAcceleratorTarget target;
controller_->Register({accelerator_a}, &target);
ui::EventSink* sink =
Shell::GetPrimaryRootWindow()->GetHost()->GetEventSink();
ui::KeyEvent key_event1(ui::EventType::kKeyPressed, ui::VKEY_A, ui::EF_NONE);
ui::EventDispatchDetails details = sink->OnEventFromSource(&key_event1);
EXPECT_TRUE(key_event1.handled() || details.dispatcher_destroyed);
ui::KeyEvent key_event2 = ui::KeyEvent::FromCharacter(
'A', ui::VKEY_A, ui::DomCode::NONE, ui::EF_NONE);
details = sink->OnEventFromSource(&key_event2);
EXPECT_FALSE(key_event2.handled() || details.dispatcher_destroyed);
ui::KeyEvent key_event3(ui::EventType::kKeyReleased, ui::VKEY_A, ui::EF_NONE);
details = sink->OnEventFromSource(&key_event3);
EXPECT_FALSE(key_event3.handled() || details.dispatcher_destroyed);
EXPECT_EQ(1, target.accelerator_count());
}
TEST_F(AcceleratorControllerTest, GlobalAccelerators) {
EXPECT_TRUE(ProcessInController(
ui::Accelerator(ui::VKEY_TAB, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN)));
EXPECT_TRUE(
ProcessInController(ui::Accelerator(ui::VKEY_TAB, ui::EF_ALT_DOWN)));
EXPECT_TRUE(ProcessInController(
ui::Accelerator(ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_NONE)));
const ui::Accelerator volume_mute(ui::VKEY_VOLUME_MUTE, ui::EF_NONE);
const ui::Accelerator volume_down(ui::VKEY_VOLUME_DOWN, ui::EF_NONE);
const ui::Accelerator volume_up(ui::VKEY_VOLUME_UP, ui::EF_NONE);
{
base::UserActionTester user_action_tester;
auto* history = controller_->GetAcceleratorHistory();
EXPECT_EQ(0, user_action_tester.GetActionCount("Accel_VolumeMute_F8"));
EXPECT_TRUE(ProcessInController(volume_mute));
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeMute_F8"));
EXPECT_EQ(volume_mute, history->current_accelerator());
EXPECT_EQ(0, user_action_tester.GetActionCount("Accel_VolumeDown_F9"));
EXPECT_TRUE(ProcessInController(volume_down));
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeDown_F9"));
EXPECT_EQ(volume_down, history->current_accelerator());
EXPECT_EQ(0, user_action_tester.GetActionCount("Accel_VolumeUp_F10"));
EXPECT_TRUE(ProcessInController(volume_up));
EXPECT_EQ(volume_up, history->current_accelerator());
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeUp_F10"));
}
const ui::Accelerator brightness_down(ui::VKEY_BRIGHTNESS_DOWN, ui::EF_NONE);
const ui::Accelerator brightness_up(ui::VKEY_BRIGHTNESS_UP, ui::EF_NONE);
{
DummyBrightnessControlDelegate* delegate =
new DummyBrightnessControlDelegate;
SetBrightnessControlDelegate(
std::unique_ptr<BrightnessControlDelegate>(delegate));
EXPECT_EQ(0, delegate->handle_brightness_down_count());
EXPECT_TRUE(ProcessInController(brightness_down));
EXPECT_EQ(1, delegate->handle_brightness_down_count());
EXPECT_EQ(brightness_down, delegate->last_accelerator());
EXPECT_EQ(0, delegate->handle_brightness_up_count());
EXPECT_TRUE(ProcessInController(brightness_up));
EXPECT_EQ(1, delegate->handle_brightness_up_count());
EXPECT_EQ(brightness_up, delegate->last_accelerator());
}
const ui::Accelerator alt_brightness_down(ui::VKEY_BRIGHTNESS_DOWN,
ui::EF_ALT_DOWN);
const ui::Accelerator alt_brightness_up(ui::VKEY_BRIGHTNESS_UP,
ui::EF_ALT_DOWN);
const ui::Accelerator toggle_keyboard_backlight(ui::VKEY_KBD_BACKLIGHT_TOGGLE,
ui::EF_NONE);
{
EXPECT_TRUE(ProcessInController(alt_brightness_down));
EXPECT_TRUE(ProcessInController(alt_brightness_up));
EXPECT_TRUE(ProcessInController(toggle_keyboard_backlight));
DummyKeyboardBrightnessControlDelegate* delegate =
new DummyKeyboardBrightnessControlDelegate;
SetKeyboardBrightnessControlDelegate(
std::unique_ptr<KeyboardBrightnessControlDelegate>(delegate));
EXPECT_EQ(0, delegate->handle_keyboard_brightness_down_count());
EXPECT_TRUE(ProcessInController(alt_brightness_down));
EXPECT_EQ(1, delegate->handle_keyboard_brightness_down_count());
EXPECT_EQ(alt_brightness_down, delegate->last_accelerator());
EXPECT_EQ(0, delegate->handle_keyboard_brightness_up_count());
EXPECT_TRUE(ProcessInController(alt_brightness_up));
EXPECT_EQ(1, delegate->handle_keyboard_brightness_up_count());
EXPECT_EQ(alt_brightness_up, delegate->last_accelerator());
EXPECT_EQ(0, delegate->handle_toggle_keyboard_backlight_count());
EXPECT_TRUE(ProcessInController(toggle_keyboard_backlight));
EXPECT_EQ(1, delegate->handle_toggle_keyboard_backlight_count());
EXPECT_EQ(toggle_keyboard_backlight, delegate->last_accelerator());
}
ExitWarningHandler* ewh = test_api_->GetExitWarningHandler();
ASSERT_TRUE(ewh);
StubForTest(ewh);
EXPECT_TRUE(is_idle(ewh));
EXPECT_FALSE(is_ui_shown(ewh));
EXPECT_TRUE(ProcessInController(
ui::Accelerator(ui::VKEY_Q, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN)));
EXPECT_FALSE(is_idle(ewh));
EXPECT_TRUE(is_ui_shown(ewh));
SimulateTimerExpired(ewh);
EXPECT_TRUE(is_idle(ewh));
EXPECT_FALSE(is_ui_shown(ewh));
Reset(ewh);
EXPECT_TRUE(
ProcessInController(ui::Accelerator(ui::VKEY_T, ui::EF_CONTROL_DOWN)));
EXPECT_TRUE(ProcessInController(
ui::Accelerator(ui::VKEY_N, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN)));
EXPECT_TRUE(
ProcessInController(ui::Accelerator(ui::VKEY_N, ui::EF_CONTROL_DOWN)));
EXPECT_TRUE(ProcessInController(
ui::Accelerator(ui::VKEY_T, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN)));
EXPECT_TRUE(ProcessInController(
ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN)));
EXPECT_TRUE(ProcessInController(
ui::Accelerator(ui::VKEY_M, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN)));
EXPECT_TRUE(
ProcessInController(ui::Accelerator(ui::VKEY_L, ui::EF_COMMAND_DOWN)));
message_center::MessageCenter::Get()->RemoveAllNotifications(
false , message_center::MessageCenter::RemoveType::ALL);
}
TEST_F(AcceleratorControllerTest, GlobalAcceleratorsToggleAppList) {
test_api_->SetCanHandleLauncher(true);
EXPECT_FALSE(
ProcessInController(ui::Accelerator(ui::VKEY_LWIN, ui::EF_NONE)));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(ui::VKEY_LWIN, GetCurrentAccelerator().key_code());
GetAppListTestHelper()->CheckVisibility(false);
EXPECT_TRUE(ProcessInController(
CreateReleaseAccelerator(ui::VKEY_LWIN, ui::EF_NONE)));
base::RunLoop().RunUntilIdle();
GetAppListTestHelper()->CheckVisibility(true);
EXPECT_TRUE(ProcessInController(
CreateReleaseAccelerator(ui::VKEY_LWIN, ui::EF_NONE)));
base::RunLoop().RunUntilIdle();
GetAppListTestHelper()->CheckVisibility(false);
EXPECT_TRUE(ProcessInController(
ui::Accelerator(ui::VKEY_BROWSER_SEARCH, ui::EF_NONE)));
base::RunLoop().RunUntilIdle();
GetAppListTestHelper()->CheckVisibility(true);
EXPECT_FALSE(ProcessInController(
CreateReleaseAccelerator(ui::VKEY_BROWSER_SEARCH, ui::EF_NONE)));
base::RunLoop().RunUntilIdle();
GetAppListTestHelper()->CheckVisibility(true);
GetAppListTestHelper()->DismissAndRunLoop();
EXPECT_FALSE(
ProcessInController(ui::Accelerator(ui::VKEY_RWIN, ui::EF_NONE)));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(ui::VKEY_RWIN, GetCurrentAccelerator().key_code());
GetAppListTestHelper()->CheckVisibility(false);
EXPECT_TRUE(ProcessInController(
CreateReleaseAccelerator(ui::VKEY_RWIN, ui::EF_NONE)));
base::RunLoop().RunUntilIdle();
GetAppListTestHelper()->CheckVisibility(true);
EXPECT_EQ(ui::VKEY_RWIN, GetPreviousAccelerator().key_code());
}
TEST_F(AcceleratorControllerTest, GlobalAcceleratorsToggleQuickSettings) {
UnifiedSystemTray* tray =
StatusAreaWidgetTestHelper::GetStatusAreaWidget()->unified_system_tray();
auto* generator = GetEventGenerator();
generator->PressKey(ui::VKEY_S, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(tray->IsBubbleShown());
generator->PressKey(ui::VKEY_S, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(tray->IsBubbleShown());
}
TEST_F(AcceleratorControllerTest, ToggleMultitaskMenu) {
SimulateUserLogin({"fakeuser"});
test_api_->ObserveAcceleratorUpdates();
Shell::Get()->ash_accelerator_configuration()->Initialize();
std::unique_ptr<aura::Window> window = CreateAppWindow();
ui::Accelerator accelerator(ui::VKEY_Z, ui::EF_COMMAND_DOWN);
EXPECT_TRUE(ProcessInController(accelerator));
auto* frame_view = FrameViewAsh::Get(window.get());
auto* size_button = static_cast<chromeos::FrameSizeButton*>(
frame_view->GetHeaderView()->caption_button_container()->size_button());
ASSERT_TRUE(size_button->IsMultitaskMenuShown());
EXPECT_TRUE(ProcessInController(accelerator));
ASSERT_FALSE(size_button->IsMultitaskMenuShown());
}
class GlobalAcceleratorsToggleLauncher
: public AcceleratorControllerTest,
public testing::WithParamInterface<
std::pair<ui::KeyboardCode, ui::Accelerator::KeyState>> {
public:
GlobalAcceleratorsToggleLauncher() {
std::tie(key_, key_state_) = GetParam();
}
protected:
ui::KeyboardCode key_;
ui::Accelerator::KeyState key_state_;
};
INSTANTIATE_TEST_SUITE_P(
All,
GlobalAcceleratorsToggleLauncher,
testing::Values(std::make_pair(ui::VKEY_LWIN,
ui::Accelerator::KeyState::RELEASED),
std::make_pair(ui::VKEY_BROWSER_SEARCH,
ui::Accelerator::KeyState::PRESSED),
std::make_pair(ui::VKEY_ALL_APPLICATIONS,
ui::Accelerator::KeyState::PRESSED)));
TEST_P(GlobalAcceleratorsToggleLauncher, ToggleLauncher) {
test_api_->SetCanHandleLauncher(true);
EXPECT_TRUE(
ProcessInController(ui::Accelerator(key_, ui::EF_NONE, key_state_)));
base::RunLoop().RunUntilIdle();
GetAppListTestHelper()->CheckVisibility(true);
EXPECT_TRUE(
ProcessInController(ui::Accelerator(key_, ui::EF_NONE, key_state_)));
base::RunLoop().RunUntilIdle();
GetAppListTestHelper()->CheckVisibility(false);
}
TEST_P(GlobalAcceleratorsToggleLauncher, PreventProcessingShortcuts) {
test_api_->SetCanHandleLauncher(true);
controller_->SetPreventProcessingAccelerators(true);
EXPECT_FALSE(
ProcessInController(ui::Accelerator(key_, ui::EF_NONE, key_state_)));
base::RunLoop().RunUntilIdle();
GetAppListTestHelper()->CheckVisibility(false);
controller_->SetPreventProcessingAccelerators(false);
EXPECT_TRUE(
ProcessInController(ui::Accelerator(key_, ui::EF_NONE, key_state_)));
base::RunLoop().RunUntilIdle();
GetAppListTestHelper()->CheckVisibility(true);
EXPECT_TRUE(
ProcessInController(ui::Accelerator(key_, ui::EF_NONE, key_state_)));
base::RunLoop().RunUntilIdle();
GetAppListTestHelper()->CheckVisibility(false);
}
TEST_F(AcceleratorControllerTest, ImeGlobalAccelerators) {
ASSERT_EQ(0u, Shell::Get()->ime_controller()->GetVisibleImes().size());
ui::Accelerator control_space_down(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
ui::Accelerator control_space_up(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
control_space_up.set_key_state(ui::Accelerator::KeyState::RELEASED);
ui::Accelerator control_shift_space(ui::VKEY_SPACE,
ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_FALSE(ProcessInController(control_space_down));
EXPECT_FALSE(ProcessInController(control_space_up));
EXPECT_FALSE(ProcessInController(control_shift_space));
AddNotVisibleTestIme();
ASSERT_EQ(0u, Shell::Get()->ime_controller()->GetVisibleImes().size());
EXPECT_FALSE(ProcessInController(control_space_down));
EXPECT_FALSE(ProcessInController(control_space_up));
EXPECT_FALSE(ProcessInController(control_shift_space));
AddTestImes();
EXPECT_TRUE(ProcessInController(control_space_down));
EXPECT_TRUE(ProcessInController(control_space_up));
EXPECT_TRUE(ProcessInController(control_shift_space));
AddNotVisibleTestIme();
EXPECT_TRUE(ProcessInController(control_space_down));
EXPECT_TRUE(ProcessInController(control_space_up));
EXPECT_TRUE(ProcessInController(control_shift_space));
}
TEST_F(AcceleratorControllerTest, ImeGlobalAcceleratorsWorkaround139556) {
const ui::Accelerator shift_alt_return_press(
ui::VKEY_RETURN, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
EXPECT_FALSE(ProcessInController(shift_alt_return_press));
const ui::Accelerator shift_alt_space_press(
ui::VKEY_SPACE, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
EXPECT_FALSE(ProcessInController(shift_alt_space_press));
}
TEST_F(AcceleratorControllerTest, PreferredReservedAccelerators) {
EXPECT_TRUE(
controller_->IsReserved(ui::Accelerator(ui::VKEY_POWER, ui::EF_NONE)));
EXPECT_FALSE(
controller_->IsPreferred(ui::Accelerator(ui::VKEY_POWER, ui::EF_NONE)));
EXPECT_TRUE(
controller_->IsReserved(ui::Accelerator(ui::VKEY_SLEEP, ui::EF_NONE)));
EXPECT_FALSE(
controller_->IsPreferred(ui::Accelerator(ui::VKEY_SLEEP, ui::EF_NONE)));
EXPECT_TRUE(
controller_->IsReserved(ui::Accelerator(ui::VKEY_F13, ui::EF_NONE)));
EXPECT_FALSE(
controller_->IsPreferred(ui::Accelerator(ui::VKEY_F13, ui::EF_NONE)));
EXPECT_FALSE(
controller_->IsReserved(ui::Accelerator(ui::VKEY_TAB, ui::EF_ALT_DOWN)));
EXPECT_FALSE(controller_->IsReserved(
ui::Accelerator(ui::VKEY_TAB, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN)));
EXPECT_TRUE(
controller_->IsPreferred(ui::Accelerator(ui::VKEY_TAB, ui::EF_ALT_DOWN)));
EXPECT_TRUE(controller_->IsPreferred(
ui::Accelerator(ui::VKEY_TAB, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN)));
EXPECT_FALSE(
controller_->IsReserved(ui::Accelerator(ui::VKEY_SNAPSHOT, ui::EF_NONE)));
EXPECT_FALSE(controller_->IsPreferred(
ui::Accelerator(ui::VKEY_SNAPSHOT, ui::EF_NONE)));
EXPECT_FALSE(
controller_->IsReserved(ui::Accelerator(ui::VKEY_TAB, ui::EF_NONE)));
EXPECT_FALSE(
controller_->IsPreferred(ui::Accelerator(ui::VKEY_TAB, ui::EF_NONE)));
EXPECT_FALSE(
controller_->IsReserved(ui::Accelerator(ui::VKEY_A, ui::EF_NONE)));
EXPECT_FALSE(
controller_->IsPreferred(ui::Accelerator(ui::VKEY_A, ui::EF_NONE)));
}
TEST_F(AcceleratorControllerTest, SideVolumeButtonLocation) {
EXPECT_TRUE(test_api_->GetSideVolumeButtonLocation().region.empty());
EXPECT_TRUE(test_api_->GetSideVolumeButtonLocation().side.empty());
base::Value::Dict location;
location.Set(kVolumeButtonRegion, kVolumeButtonRegionScreen);
location.Set(kVolumeButtonSide, kVolumeButtonSideLeft);
base::ScopedTempDir file_tmp_dir;
ASSERT_TRUE(file_tmp_dir.CreateUniqueTempDir());
base::FilePath file_path = file_tmp_dir.GetPath().Append("location.json");
ASSERT_TRUE(WriteJsonFile(file_path, base::WriteJson(location).value_or("")));
EXPECT_TRUE(base::PathExists(file_path));
test_api_->SetSideVolumeButtonFilePath(file_path);
EXPECT_EQ(kVolumeButtonRegionScreen,
test_api_->GetSideVolumeButtonLocation().region);
EXPECT_EQ(kVolumeButtonSideLeft,
test_api_->GetSideVolumeButtonLocation().side);
base::DeleteFile(file_path);
}
TEST_F(AcceleratorControllerTest, TabletModeVolumeAdjustHistogram) {
ash::TabletModeControllerTestApi().EnterTabletMode();
base::HistogramTester histogram_tester;
const ui::Accelerator kVolumeDown(ui::VKEY_VOLUME_DOWN, ui::EF_NONE);
const ui::Accelerator kVolumeUp(ui::VKEY_VOLUME_UP, ui::EF_NONE);
ProcessInController(kVolumeUp);
ProcessInController(kVolumeDown);
ProcessInController(kVolumeDown);
EXPECT_TRUE(test_api_->TriggerTabletModeVolumeAdjustTimer());
histogram_tester.ExpectBucketCount(
kTabletCountOfVolumeAdjustType,
TabletModeVolumeAdjustType::kAccidentalAdjustWithSwapEnabled, 1);
ProcessInController(kVolumeUp);
ProcessInController(kVolumeUp);
ProcessInController(kVolumeUp);
EXPECT_TRUE(test_api_->TriggerTabletModeVolumeAdjustTimer());
histogram_tester.ExpectBucketCount(
kTabletCountOfVolumeAdjustType,
TabletModeVolumeAdjustType::kNormalAdjustWithSwapEnabled, 1);
}
class SideVolumeButtonAcceleratorTest
: public AcceleratorControllerTest,
public testing::WithParamInterface<std::pair<std::string, std::string>> {
public:
static constexpr int kSideVolumeButtonId = 7;
SideVolumeButtonAcceleratorTest()
: region_(GetParam().first), side_(GetParam().second) {}
SideVolumeButtonAcceleratorTest(const SideVolumeButtonAcceleratorTest&) =
delete;
SideVolumeButtonAcceleratorTest& operator=(
const SideVolumeButtonAcceleratorTest&) = delete;
~SideVolumeButtonAcceleratorTest() override = default;
void SetUp() override {
AcceleratorControllerTest::SetUp();
ash::TabletModeControllerTestApi().EnterTabletMode();
test_api_->SetSideVolumeButtonLocation(region_, side_);
ui::DeviceDataManagerTestApi().SetUncategorizedDevices({ui::InputDevice(
kSideVolumeButtonId, ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
"cros_ec_buttons")});
}
bool IsLeftOrRightSide() const {
return side_ == kVolumeButtonSideLeft || side_ == kVolumeButtonSideRight;
}
bool IsOnKeyboard() const { return region_ == kVolumeButtonRegionKeyboard; }
private:
std::string region_, side_;
};
TEST_P(SideVolumeButtonAcceleratorTest, FlipSideVolumeButtonAction) {
display::test::ScopedSetInternalDisplayId set_internal(
display_manager(), GetPrimaryDisplay().id());
ScreenOrientationControllerTestApi test_api(
Shell::Get()->screen_orientation_controller());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapePrimary);
base::UserActionTester user_action_tester;
const ui::Accelerator volume_down(ui::VKEY_VOLUME_DOWN, ui::EF_NONE);
ASSERT_EQ(ui::ED_UNKNOWN_DEVICE, volume_down.source_device_id());
ProcessInController(volume_down);
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeDown_F9"));
user_action_tester.ResetCounts();
ui::KeyEvent event(ui::EventType::kKeyPressed, ui::VKEY_VOLUME_DOWN,
ui::DomCode::VOLUME_DOWN, 0, 2099727,
base::TimeTicks::Now());
event.set_source_device_id(kSideVolumeButtonId);
const ui::Accelerator volume_down_from_side_volume_button(event);
ProcessInController(volume_down_from_side_volume_button);
if (IsOnKeyboard() && IsLeftOrRightSide())
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeUp_F10"));
else
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeDown_F9"));
user_action_tester.ResetCounts();
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitPrimary);
ProcessInController(volume_down_from_side_volume_button);
if (!IsOnKeyboard() && IsLeftOrRightSide())
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeDown_F9"));
else
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeUp_F10"));
user_action_tester.ResetCounts();
test_api.SetDisplayRotation(display::Display::ROTATE_180,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapeSecondary);
ProcessInController(volume_down_from_side_volume_button);
if (IsOnKeyboard() && IsLeftOrRightSide())
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeDown_F9"));
else
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeUp_F10"));
user_action_tester.ResetCounts();
test_api.SetDisplayRotation(display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitSecondary);
ProcessInController(volume_down_from_side_volume_button);
if (!IsOnKeyboard() && IsLeftOrRightSide())
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeUp_F10"));
else
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeDown_F9"));
}
INSTANTIATE_TEST_SUITE_P(
AshSideVolumeButton,
SideVolumeButtonAcceleratorTest,
testing::ValuesIn(
{std::pair<std::string, std::string>(kVolumeButtonRegionKeyboard,
kVolumeButtonSideLeft),
std::pair<std::string, std::string>(kVolumeButtonRegionKeyboard,
kVolumeButtonSideRight),
std::pair<std::string, std::string>(kVolumeButtonRegionKeyboard,
kVolumeButtonSideBottom),
std::pair<std::string, std::string>(kVolumeButtonRegionScreen,
kVolumeButtonSideLeft),
std::pair<std::string, std::string>(kVolumeButtonRegionScreen,
kVolumeButtonSideRight),
std::pair<std::string, std::string>(kVolumeButtonRegionScreen,
kVolumeButtonSideTop),
std::pair<std::string, std::string>(kVolumeButtonRegionScreen,
kVolumeButtonSideBottom)}));
TEST_F(AcceleratorControllerTest, PressAndReleasePowerButtonWithFunctionKey) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
{features::kModifierSplit, features::kPeripheralCustomization}, {});
const int kKeyboardDeviceIdWithFunction = 123;
const int kKeyboardDeviceId = 456;
const ui::KeyboardDevice keyboard_with_function(
kKeyboardDeviceIdWithFunction, ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
"test keyboard with function key",
"",
base::FilePath("/devices/platform/i8042/serio2/input/input1"),
-1,
-1, -1,
true,
true);
const ui::KeyboardDevice keyboard(
kKeyboardDeviceId, ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
"test keyboard with function key",
"",
base::FilePath("/devices/platform/i8042/serio2/input/input1"),
-1,
-1, -1,
true,
false);
ui::DeviceDataManagerTestApi().SetKeyboardDevices({keyboard_with_function});
ui::KeyEvent press_event_with_function(ui::EventType::kKeyPressed,
ui::VKEY_F13,
ui::EF_NONE);
press_event_with_function.set_source_device_id(kKeyboardDeviceIdWithFunction);
const ui::Accelerator press_f13_with_function(press_event_with_function);
EXPECT_FALSE(ProcessInController(press_f13_with_function));
ui::KeyEvent release_event_with_function(ui::EventType::kKeyReleased,
ui::VKEY_F13,
ui::EF_NONE);
release_event_with_function.set_source_device_id(
kKeyboardDeviceIdWithFunction);
const ui::Accelerator release_f13_with_function(release_event_with_function);
EXPECT_FALSE(ProcessInController(release_f13_with_function));
ui::DeviceDataManagerTestApi().SetKeyboardDevices(
{keyboard_with_function, keyboard});
ui::KeyEvent press_event(ui::EventType::kKeyPressed, ui::VKEY_F13,
ui::EF_NONE);
press_event.set_source_device_id(kKeyboardDeviceId);
const ui::Accelerator press_f13(press_event);
EXPECT_TRUE(ProcessInController(press_f13));
ui::KeyEvent release_event(ui::EventType::kKeyReleased, ui::VKEY_F13,
ui::EF_NONE);
release_event.set_source_device_id(kKeyboardDeviceId);
const ui::Accelerator release_f13(press_event);
EXPECT_TRUE(ProcessInController(release_f13));
}
TEST_F(AcceleratorControllerTest, ToggleCapsLockAcceleratorsWithFunctionKey) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
{features::kModifierSplit, features::kShortcutStateMachines,
features::kPeripheralCustomization},
{});
AnchoredNudgeManagerImpl* nudge_manager =
Shell::Get()->anchored_nudge_manager();
ASSERT_TRUE(nudge_manager);
const int kKeyboardDeviceId = 123;
const ui::KeyboardDevice keyboard(
kKeyboardDeviceId, ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
"test keyboard with function key",
"",
base::FilePath("/devices/platform/i8042/serio2/input/input1"),
-1,
-1, -1,
true,
true);
ui::DeviceDataManagerTestApi().SetKeyboardDevices({keyboard});
test_api_->SetCanHandleCapsLock(true);
ImeControllerImpl* controller = Shell::Get()->ime_controller();
TestImeControllerClient client;
controller->SetClient(&client);
EXPECT_EQ(0, client.set_caps_lock_count_);
ui::KeyEvent press_event(ui::EventType::kKeyPressed, ui::VKEY_LWIN,
ui::EF_ALT_DOWN);
press_event.set_source_device_id(kKeyboardDeviceId);
const ui::Accelerator press_search_after_alt(press_event);
EXPECT_FALSE(ProcessInController(press_search_after_alt));
ui::KeyEvent release_event(ui::EventType::kKeyReleased, ui::VKEY_LWIN,
ui::EF_ALT_DOWN);
release_event.set_source_device_id(kKeyboardDeviceId);
const ui::Accelerator release_search_after_alt(release_event);
EXPECT_FALSE(ProcessInController(release_search_after_alt));
EXPECT_EQ(0, client.set_caps_lock_count_);
EXPECT_FALSE(controller->IsCapsLockEnabled());
EXPECT_TRUE(nudge_manager->GetNudgeIfShown(kCapsLockNoMatchNudgeId));
nudge_manager->Cancel(kCapsLockNoMatchNudgeId);
feature_list.Reset();
}
TEST_F(AcceleratorControllerTest, ToggleCapsLockAccelerators) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(features::kShortcutStateMachines);
ImeControllerImpl* controller = Shell::Get()->ime_controller();
TestImeControllerClient client;
controller->SetClient(&client);
EXPECT_EQ(0, client.set_caps_lock_count_);
const ui::Accelerator press_alt_then_search(ui::VKEY_LWIN, ui::EF_ALT_DOWN);
EXPECT_FALSE(ProcessInController(press_alt_then_search));
const ui::Accelerator release_search_before_alt(
CreateReleaseAccelerator(ui::VKEY_LWIN, ui::EF_ALT_DOWN));
EXPECT_TRUE(ProcessInController(release_search_before_alt));
EXPECT_EQ(1, client.set_caps_lock_count_);
EXPECT_TRUE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
const ui::Accelerator press_search_then_alt(ui::VKEY_MENU,
ui::EF_COMMAND_DOWN);
EXPECT_FALSE(ProcessInController(press_search_then_alt));
EXPECT_TRUE(ProcessInController(release_search_before_alt));
EXPECT_EQ(2, client.set_caps_lock_count_);
EXPECT_TRUE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
EXPECT_FALSE(ProcessInController(press_alt_then_search));
const ui::Accelerator release_alt_before_search(
CreateReleaseAccelerator(ui::VKEY_MENU, ui::EF_COMMAND_DOWN));
EXPECT_TRUE(ProcessInController(release_alt_before_search));
EXPECT_EQ(3, client.set_caps_lock_count_);
EXPECT_TRUE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
EXPECT_FALSE(ProcessInController(press_search_then_alt));
EXPECT_TRUE(ProcessInController(release_alt_before_search));
EXPECT_EQ(4, client.set_caps_lock_count_);
EXPECT_TRUE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
ui::test::EventGenerator* generator = GetEventGenerator();
generator->PressKey(ui::VKEY_M, ui::EF_NONE);
generator->PressKey(ui::VKEY_MENU, ui::EF_NONE);
generator->PressKey(ui::VKEY_LWIN, ui::EF_ALT_DOWN);
generator->ReleaseKey(ui::VKEY_MENU, ui::EF_COMMAND_DOWN);
EXPECT_FALSE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
generator->ReleaseKey(ui::VKEY_M, ui::EF_NONE);
generator->ReleaseKey(ui::VKEY_LWIN, ui::EF_ALT_DOWN);
{
const ui::Accelerator press_partial_screenshot_shortcut(
ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN);
EXPECT_TRUE(ProcessInController(press_partial_screenshot_shortcut));
const ui::Accelerator release_partial_screenshot_shortcut =
CreateReleaseAccelerator(ui::VKEY_MEDIA_LAUNCH_APP1,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN);
EXPECT_FALSE(ProcessInController(release_partial_screenshot_shortcut));
generator->MoveMouseTo(0, 0);
generator->PressLeftButton();
generator->MoveMouseTo(10, 10);
generator->ReleaseLeftButton();
auto* capture_mode_controller = CaptureModeController::Get();
EXPECT_TRUE(capture_mode_controller->IsActive());
EXPECT_EQ(CaptureModeSource::kRegion, capture_mode_controller->source());
EXPECT_FALSE(ProcessInController(press_search_then_alt));
EXPECT_TRUE(ProcessInController(release_search_before_alt));
EXPECT_EQ(5, client.set_caps_lock_count_);
EXPECT_TRUE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
}
generator->PressKey(ui::VKEY_PROCESSKEY, ui::EF_IME_FABRICATED_KEY);
generator->ReleaseKey(ui::VKEY_UNKNOWN, ui::EF_IME_FABRICATED_KEY);
EXPECT_FALSE(ProcessInController(press_search_then_alt));
EXPECT_TRUE(ProcessInController(release_search_before_alt));
EXPECT_EQ(6, client.set_caps_lock_count_);
EXPECT_TRUE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
}
TEST_F(AcceleratorControllerTest, ToggleCapsLockAcceleratorsStateMachines) {
base::test::ScopedFeatureList feature_list_;
feature_list_.InitAndEnableFeature(features::kShortcutStateMachines);
ImeControllerImpl* controller = Shell::Get()->ime_controller();
TestImeControllerClient client;
controller->SetClient(&client);
EXPECT_EQ(0, client.set_caps_lock_count_);
test_api_->SetCanHandleCapsLock(true);
const ui::Accelerator press_alt_then_search(ui::VKEY_LWIN, ui::EF_ALT_DOWN);
EXPECT_FALSE(ProcessInController(press_alt_then_search));
const ui::Accelerator release_search_before_alt(
CreateReleaseAccelerator(ui::VKEY_LWIN, ui::EF_ALT_DOWN));
EXPECT_TRUE(ProcessInController(release_search_before_alt));
EXPECT_EQ(1, client.set_caps_lock_count_);
EXPECT_TRUE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
const ui::Accelerator press_search_then_alt(ui::VKEY_MENU,
ui::EF_COMMAND_DOWN);
EXPECT_FALSE(ProcessInController(press_search_then_alt));
EXPECT_TRUE(ProcessInController(release_search_before_alt));
EXPECT_EQ(2, client.set_caps_lock_count_);
EXPECT_TRUE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
EXPECT_FALSE(ProcessInController(press_alt_then_search));
const ui::Accelerator release_alt_before_search(
CreateReleaseAccelerator(ui::VKEY_MENU, ui::EF_COMMAND_DOWN));
EXPECT_TRUE(ProcessInController(release_alt_before_search));
EXPECT_EQ(3, client.set_caps_lock_count_);
EXPECT_TRUE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
EXPECT_FALSE(ProcessInController(press_search_then_alt));
EXPECT_TRUE(ProcessInController(release_alt_before_search));
EXPECT_EQ(4, client.set_caps_lock_count_);
EXPECT_TRUE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
test_api_->SetCanHandleCapsLock(false);
EXPECT_FALSE(ProcessInController(press_search_then_alt));
EXPECT_FALSE(ProcessInController(release_alt_before_search));
EXPECT_EQ(4, client.set_caps_lock_count_);
EXPECT_FALSE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
EXPECT_FALSE(ProcessInController(press_alt_then_search));
EXPECT_FALSE(ProcessInController(release_search_before_alt));
EXPECT_EQ(4, client.set_caps_lock_count_);
EXPECT_FALSE(controller->IsCapsLockEnabled());
controller->UpdateCapsLockState(false);
}
class PreferredReservedAcceleratorsTest : public AshTestBase {
public:
PreferredReservedAcceleratorsTest() = default;
PreferredReservedAcceleratorsTest(const PreferredReservedAcceleratorsTest&) =
delete;
PreferredReservedAcceleratorsTest& operator=(
const PreferredReservedAcceleratorsTest&) = delete;
~PreferredReservedAcceleratorsTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
Shell::Get()->lock_state_controller()->set_animator_for_test(
new TestSessionStateAnimator);
Shell::Get()->power_button_controller()->OnGetSwitchStates(
chromeos::PowerManagerClient::SwitchStates{
chromeos::PowerManagerClient::LidState::OPEN,
chromeos::PowerManagerClient::TabletMode::ON});
}
};
TEST_F(PreferredReservedAcceleratorsTest, AcceleratorsWithFullscreen) {
aura::Window* w1 = CreateTestWindowInShell({.window_id = 0});
aura::Window* w2 = CreateTestWindowInShell({.window_id = 1});
wm::ActivateWindow(w1);
WMEvent fullscreen(WM_EVENT_FULLSCREEN);
WindowState* w1_state = WindowState::Get(w1);
w1_state->OnWMEvent(&fullscreen);
ASSERT_TRUE(w1_state->IsFullscreen());
ui::test::EventGenerator* generator = GetEventGenerator();
ash::TabletModeControllerTestApi().EnterTabletMode();
PowerButtonControllerTestApi test_api(
Shell::Get()->power_button_controller());
EXPECT_FALSE(test_api.PowerButtonMenuTimerIsRunning());
generator->PressKey(ui::VKEY_POWER, ui::EF_NONE);
EXPECT_TRUE(test_api.PowerButtonMenuTimerIsRunning());
auto press_and_release_alt_tab = [&generator]() {
generator->PressKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
generator->ReleaseKey(ui::VKEY_MENU, ui::EF_NONE);
};
ASSERT_EQ(w1, window_util::GetActiveWindow());
press_and_release_alt_tab();
ASSERT_EQ(w1, window_util::GetActiveWindow());
ASSERT_NE(w2, window_util::GetActiveWindow());
generator->PressKey(ui::VKEY_A, ui::EF_NONE);
generator->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
WMEvent normal(WM_EVENT_NORMAL);
w1_state->OnWMEvent(&normal);
ASSERT_FALSE(w1_state->IsFullscreen());
EXPECT_EQ(w1, window_util::GetActiveWindow());
press_and_release_alt_tab();
ASSERT_NE(w1, window_util::GetActiveWindow());
ASSERT_EQ(w2, window_util::GetActiveWindow());
}
TEST_F(PreferredReservedAcceleratorsTest, AcceleratorsWithPinned) {
aura::Window* w1 = CreateTestWindowInShell({.window_id = 0});
aura::Window* w2 = CreateTestWindowInShell({.window_id = 1});
wm::ActivateWindow(w1);
{
WMEvent pin_event(WM_EVENT_PIN);
WindowState* w1_state = WindowState::Get(w1);
w1_state->OnWMEvent(&pin_event);
ASSERT_TRUE(w1_state->IsPinned());
}
ui::test::EventGenerator* generator = GetEventGenerator();
ash::TabletModeControllerTestApi().EnterTabletMode();
PowerButtonControllerTestApi test_api(
Shell::Get()->power_button_controller());
EXPECT_FALSE(test_api.PowerButtonMenuTimerIsRunning());
generator->PressKey(ui::VKEY_POWER, ui::EF_NONE);
EXPECT_TRUE(test_api.PowerButtonMenuTimerIsRunning());
ASSERT_EQ(w1, window_util::GetActiveWindow());
generator->PressKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
generator->ReleaseKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
ASSERT_EQ(w1, window_util::GetActiveWindow());
ASSERT_NE(w2, window_util::GetActiveWindow());
}
TEST_F(AcceleratorControllerTest, DisallowedAtModalWindow) {
std::set<AcceleratorAction> all_actions;
for (const AcceleratorData& data : kAcceleratorData) {
all_actions.insert(data.action);
}
std::set<AcceleratorAction> all_debug_actions;
for (const AcceleratorData& data : kDebugAcceleratorData) {
all_debug_actions.insert(data.action);
}
std::set<AcceleratorAction> all_dev_actions;
for (const AcceleratorData& data : kDeveloperAcceleratorData) {
all_dev_actions.insert(data.action);
}
std::set<AcceleratorAction> actionsAllowedAtModalWindow;
for (const AcceleratorAction& action : kActionsAllowedAtModalWindow) {
actionsAllowedAtModalWindow.insert(action);
}
for (const auto& action : actionsAllowedAtModalWindow) {
EXPECT_TRUE(base::Contains(all_actions, action) ||
base::Contains(all_debug_actions, action) ||
base::Contains(all_dev_actions, action))
<< " action from kActionsAllowedAtModalWindow"
<< " not found in kAcceleratorData, kDebugAcceleratorData or"
<< " kDeveloperAcceleratorData action: " << action;
}
std::unique_ptr<aura::Window> window(
CreateTestWindowInShell({.bounds = {5, 5, 20, 20}, .window_id = 0}));
wm::ActivateWindow(window.get());
ShellTestApi().SimulateModalWindowOpenForTest(true);
for (const auto& action : all_actions) {
if (!base::Contains(actionsAllowedAtModalWindow, action)) {
EXPECT_TRUE(controller_->PerformActionIfEnabled(action, {}))
<< " for action (disallowed at modal window): " << action;
}
}
auto* controller = CaptureModeController::Get();
EXPECT_TRUE(ProcessInController(ui::Accelerator(
ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN)));
EXPECT_TRUE(controller->IsActive());
EXPECT_EQ(CaptureModeSource::kRegion, controller->source());
controller->Stop();
EXPECT_TRUE(ProcessInController(ui::Accelerator(
ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN)));
EXPECT_TRUE(controller->IsActive());
EXPECT_EQ(CaptureModeSource::kWindow, controller->source());
controller->Stop();
EXPECT_TRUE(
ProcessInController(ui::Accelerator(ui::VKEY_SNAPSHOT, ui::EF_NONE)));
EXPECT_TRUE(controller->IsActive());
EXPECT_EQ(CaptureModeSource::kWindow, controller->source());
EXPECT_TRUE(
ProcessInController(ui::Accelerator(ui::VKEY_SNAPSHOT, ui::EF_NONE)));
ASSERT_FALSE(controller->IsActive());
EXPECT_TRUE(ProcessInController(
ui::Accelerator(ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_CONTROL_DOWN)));
EXPECT_FALSE(controller->IsActive());
base::RunLoop run_loop;
CaptureModeTestApi().SetOnCaptureFileSavedCallback(base::BindLambdaForTesting(
[&run_loop](const base::FilePath& path) { run_loop.Quit(); }));
run_loop.Run();
const ui::Accelerator brightness_down(ui::VKEY_BRIGHTNESS_DOWN, ui::EF_NONE);
const ui::Accelerator brightness_up(ui::VKEY_BRIGHTNESS_UP, ui::EF_NONE);
{
DummyBrightnessControlDelegate* delegate =
new DummyBrightnessControlDelegate;
SetBrightnessControlDelegate(
std::unique_ptr<BrightnessControlDelegate>(delegate));
EXPECT_EQ(0, delegate->handle_brightness_down_count());
EXPECT_TRUE(ProcessInController(brightness_down));
EXPECT_EQ(1, delegate->handle_brightness_down_count());
EXPECT_EQ(brightness_down, delegate->last_accelerator());
EXPECT_EQ(0, delegate->handle_brightness_up_count());
EXPECT_TRUE(ProcessInController(brightness_up));
EXPECT_EQ(1, delegate->handle_brightness_up_count());
EXPECT_EQ(brightness_up, delegate->last_accelerator());
}
const ui::Accelerator volume_mute(ui::VKEY_VOLUME_MUTE, ui::EF_NONE);
const ui::Accelerator volume_down(ui::VKEY_VOLUME_DOWN, ui::EF_NONE);
const ui::Accelerator volume_up(ui::VKEY_VOLUME_UP, ui::EF_NONE);
{
base::UserActionTester user_action_tester;
auto* history = controller_->GetAcceleratorHistory();
EXPECT_EQ(0, user_action_tester.GetActionCount("Accel_VolumeMute_F8"));
EXPECT_TRUE(ProcessInController(volume_mute));
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeMute_F8"));
EXPECT_EQ(volume_mute, history->current_accelerator());
EXPECT_EQ(0, user_action_tester.GetActionCount("Accel_VolumeDown_F9"));
EXPECT_TRUE(ProcessInController(volume_down));
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeDown_F9"));
EXPECT_EQ(volume_down, history->current_accelerator());
EXPECT_EQ(0, user_action_tester.GetActionCount("Accel_VolumeUp_F10"));
EXPECT_TRUE(ProcessInController(volume_up));
EXPECT_EQ(volume_up, history->current_accelerator());
EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeUp_F10"));
}
}
TEST_F(AcceleratorControllerTest, DisallowedWithNoWindow) {
AccessibilityController* accessibility_controller =
Shell::Get()->accessibility_controller();
TestAccessibilityControllerClient client;
std::set<AcceleratorAction> actions_needing_window;
for (const AcceleratorAction& action : kActionsNeedingWindow) {
actions_needing_window.insert(action);
}
std::map<AcceleratorAction, ui::Accelerator> accelerators_needing_window;
for (const AcceleratorData& accelerator_data : kAcceleratorData) {
auto iter = actions_needing_window.find(accelerator_data.action);
if (!base::Contains(actions_needing_window, accelerator_data.action)) {
continue;
}
ui::Accelerator accelerator{accelerator_data.keycode,
accelerator_data.modifiers};
if (!accelerator_data.trigger_on_press) {
accelerator.set_key_state(ui::Accelerator::KeyState::RELEASED);
}
accelerators_needing_window[*iter] = accelerator;
}
for (const auto& iter : accelerators_needing_window) {
accessibility_controller->TriggerAccessibilityAlert(
AccessibilityAlert::NONE);
EXPECT_TRUE(controller_->PerformActionIfEnabled(iter.first, iter.second));
EXPECT_EQ(AccessibilityAlert::WINDOW_NEEDED, client.last_a11y_alert());
}
std::unique_ptr<aura::Window> window;
for (const auto& iter : accelerators_needing_window) {
window.reset(
CreateTestWindowInShell({.bounds = {5, 5, 20, 20}, .window_id = 0}));
wm::ActivateWindow(window.get());
accessibility_controller->TriggerAccessibilityAlert(
AccessibilityAlert::NONE);
controller_->PerformActionIfEnabled(iter.first, iter.second);
EXPECT_NE(AccessibilityAlert::WINDOW_NEEDED, client.last_a11y_alert());
}
for (const auto& iter : accelerators_needing_window) {
window.reset(
CreateTestWindowInShell({.bounds = {5, 5, 20, 20}, .window_id = 0}));
wm::ActivateWindow(window.get());
controller_->PerformActionIfEnabled(AcceleratorAction::kWindowMinimize, {});
accessibility_controller->TriggerAccessibilityAlert(
AccessibilityAlert::NONE);
controller_->PerformActionIfEnabled(iter.first, iter.second);
EXPECT_NE(AccessibilityAlert::WINDOW_NEEDED, client.last_a11y_alert());
}
}
TEST_F(AcceleratorControllerTest, TestDialogCancel) {
ui::Accelerator accelerator(ui::VKEY_H,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN);
AccessibilityController* accessibility_controller =
Shell::Get()->accessibility_controller();
EXPECT_FALSE(accessibility_controller->high_contrast().WasDialogAccepted());
EXPECT_FALSE(IsConfirmationDialogOpen());
EXPECT_TRUE(ProcessInController(accelerator));
EXPECT_TRUE(IsConfirmationDialogOpen());
CancelConfirmationDialog();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(accessibility_controller->high_contrast().WasDialogAccepted());
EXPECT_FALSE(IsConfirmationDialogOpen());
}
TEST_F(AcceleratorControllerTest, TestToggleHighContrast) {
ui::Accelerator accelerator(ui::VKEY_H,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN);
EXPECT_FALSE(IsConfirmationDialogOpen());
AccessibilityController* accessibility_controller =
Shell::Get()->accessibility_controller();
EXPECT_FALSE(accessibility_controller->high_contrast().WasDialogAccepted());
EXPECT_TRUE(ProcessInController(accelerator));
EXPECT_TRUE(IsConfirmationDialogOpen());
AcceptConfirmationDialog();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(ContainsHighContrastNotification());
EXPECT_FALSE(IsNotificationPinned(kHighContrastToggleAccelNotificationId));
EXPECT_TRUE(accessibility_controller->high_contrast().WasDialogAccepted());
EXPECT_FALSE(IsConfirmationDialogOpen());
EXPECT_TRUE(ProcessInController(accelerator));
EXPECT_FALSE(ContainsHighContrastNotification());
EXPECT_TRUE(accessibility_controller->high_contrast().WasDialogAccepted());
EXPECT_TRUE(ProcessInController(accelerator));
EXPECT_FALSE(IsConfirmationDialogOpen());
EXPECT_TRUE(ContainsHighContrastNotification());
RemoveAllNotifications();
}
TEST_F(AcceleratorControllerTest, CalculatorKey) {
auto observer = std::make_unique<MockAcceleratorObserver>();
auto* accelerator_controller = ash::AcceleratorController::Get();
accelerator_controller->AddObserver(observer.get());
ui::Accelerator accelerator(ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_NONE);
EXPECT_TRUE(controller_->IsRegistered(accelerator));
EXPECT_CALL(new_window_delegate(), OpenCalculator)
.WillOnce(testing::Return());
EXPECT_CALL(*observer, OnActionPerformed)
.WillOnce([](AcceleratorAction action) {
EXPECT_EQ(AcceleratorAction::kOpenCalculator, action);
});
EXPECT_TRUE(ProcessInController(accelerator));
accelerator_controller->RemoveObserver(observer.get());
}
TEST_F(AcceleratorControllerTest, ChangeIMEMode_SwitchesInputMethod) {
AddTestImes();
ImeController* controller = Shell::Get()->ime_controller();
TestImeControllerClient client;
controller->SetClient(&client);
EXPECT_EQ(0, client.next_ime_count_);
ProcessInController(ui::Accelerator(ui::VKEY_MODECHANGE, ui::EF_NONE));
EXPECT_EQ(1, client.next_ime_count_);
}
TEST_F(AcceleratorControllerTest, ToggleDoNotDisturbKey) {
base::test::ScopedFeatureList feature_list(features::kDoNotDisturbShortcut);
ASSERT_FALSE(message_center()->IsQuietMode());
accelerators::ToggleDoNotDisturb();
ASSERT_TRUE(message_center()->IsQuietMode());
accelerators::ToggleDoNotDisturb();
ASSERT_FALSE(message_center()->IsQuietMode());
}
TEST_F(AcceleratorControllerTest, OverviewBasedScreenshotMetric) {
const ui::KeyboardDevice kChromeOSKeyboardWithScreenshot(
5, ui::INPUT_DEVICE_INTERNAL, "ChromeOSKeyboardWithScreenshot");
const ui::KeyboardDevice kChromeOSKeyboardWithoutScreenshot(
10, ui::INPUT_DEVICE_BLUETOOTH, "ChromeOSKeyboardWithoutScreenshot");
const ui::KeyboardDevice kNonChromeOSKeyboard(15, ui::INPUT_DEVICE_USB,
"NonChromeOSKeyboard");
ui::DeviceDataManagerTestApi().SetKeyboardDevices(
{kChromeOSKeyboardWithoutScreenshot, kChromeOSKeyboardWithScreenshot,
kNonChromeOSKeyboard});
Shell::Get()->keyboard_capability()->SetKeyboardInfoForTesting(
kChromeOSKeyboardWithScreenshot,
{ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard,
ui::KeyboardCapability::KeyboardTopRowLayout::kKbdTopRowLayoutCustom,
{555},
{ui::TopRowActionKey::kScreenshot}});
Shell::Get()->keyboard_capability()->SetKeyboardInfoForTesting(
kChromeOSKeyboardWithoutScreenshot,
{ui::KeyboardCapability::DeviceType::kDeviceExternalChromeOsKeyboard,
ui::KeyboardCapability::KeyboardTopRowLayout::kKbdTopRowLayoutCustom,
{555},
{ui::TopRowActionKey::kOverview}});
Shell::Get()->keyboard_capability()->SetKeyboardInfoForTesting(
kNonChromeOSKeyboard,
{ui::KeyboardCapability::DeviceType::kDeviceExternalGenericKeyboard,
ui::KeyboardCapability::KeyboardTopRowLayout::kKbdTopRowLayout1,
{},
{ui::TopRowActionKey::kOverview}});
ui::KeyEvent partial_screenshot_event(
ui::EventType::kKeyPressed, ui::VKEY_MEDIA_LAUNCH_APP1,
ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
{
base::HistogramTester histogram_tester;
partial_screenshot_event.set_source_device_id(
kChromeOSKeyboardWithScreenshot.id);
controller_->Process(ui::Accelerator(partial_screenshot_event));
histogram_tester.ExpectUniqueSample(
"Ash.Accelerators.OverviewBasedScreenshot.TakePartialScreenshot",
AcceleratorControllerImpl::OverviewBasedScreenshotKeyboardType::
kChromeOSKeyboardWithScreenshot,
1);
}
{
base::HistogramTester histogram_tester;
partial_screenshot_event.set_source_device_id(
kChromeOSKeyboardWithoutScreenshot.id);
controller_->Process(ui::Accelerator(partial_screenshot_event));
histogram_tester.ExpectUniqueSample(
"Ash.Accelerators.OverviewBasedScreenshot.TakePartialScreenshot",
AcceleratorControllerImpl::OverviewBasedScreenshotKeyboardType::
kChromeOSKeyboardWithoutScreenshot,
1);
}
{
base::HistogramTester histogram_tester;
partial_screenshot_event.set_source_device_id(kNonChromeOSKeyboard.id);
controller_->Process(ui::Accelerator(partial_screenshot_event));
histogram_tester.ExpectUniqueSample(
"Ash.Accelerators.OverviewBasedScreenshot.TakePartialScreenshot",
AcceleratorControllerImpl::OverviewBasedScreenshotKeyboardType::
kNonChromeOSKeyboard,
1);
}
}
class SystemShortcutBehaviorTest : public AcceleratorControllerTest {
void SetUp() override {
AcceleratorControllerTest::SetUp();
auto user_prefs = std::make_unique<TestingPrefServiceSimple>();
user_prefs_ = user_prefs.get();
RegisterUserProfilePrefs(user_prefs->registry(), "",
true);
SimulateUserLogin({kUserEmail}, std::nullopt, std::move(user_prefs));
}
void TearDown() override {
user_prefs_ = nullptr;
AcceleratorControllerTest::TearDown();
}
protected:
raw_ptr<TestingPrefServiceSimple> user_prefs_ = nullptr;
};
TEST_F(SystemShortcutBehaviorTest, StandardSearchBasedAcceleratorProcessing) {
VoidEventHandler event_handler;
aura::Window* w1 = CreateTestWindowInShell({.window_id = 0});
w1->AddPostTargetHandler(&event_handler);
wm::ActivateWindow(w1);
ui::test::EventGenerator* generator = GetEventGenerator();
generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_D, ui::EF_COMMAND_DOWN);
EXPECT_EQ(3, event_handler.num_events_received());
}
TEST_F(SystemShortcutBehaviorTest, IgnoreCommonVdiShortcuts) {
user_prefs_->SetManagedPref(
ash::prefs::kSystemShortcutBehavior,
base::Value(static_cast<int>(
SystemShortcutBehaviorType::kIgnoreCommonVdiShortcuts)));
ui::Accelerator press_d_and_search(ui::VKEY_D, ui::EF_COMMAND_DOWN);
EXPECT_FALSE(ProcessInController(press_d_and_search));
user_prefs_->RemoveManagedPref(ash::prefs::kSystemShortcutBehavior);
EXPECT_TRUE(ProcessInController(press_d_and_search));
}
TEST_F(SystemShortcutBehaviorTest, IgnoreCommonVdiShortcutsFullscreenOnly) {
VoidEventHandler event_handler;
aura::Window* w1 = CreateTestWindowInShell({.window_id = 0});
w1->AddPostTargetHandler(&event_handler);
wm::ActivateWindow(w1);
ui::test::EventGenerator* generator = GetEventGenerator();
user_prefs_->SetManagedPref(
ash::prefs::kSystemShortcutBehavior,
base::Value(
static_cast<int>(SystemShortcutBehaviorType::
kIgnoreCommonVdiShortcutsFullscreenOnly)));
{
generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_D,
ui::EF_COMMAND_DOWN);
EXPECT_EQ(3, event_handler.num_events_received());
event_handler.ResetEventCounter();
}
WMEvent fullscreen(WM_EVENT_FULLSCREEN);
WindowState* w1_state = WindowState::Get(w1);
w1_state->OnWMEvent(&fullscreen);
ASSERT_TRUE(w1_state->IsFullscreen());
{
generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_D,
ui::EF_COMMAND_DOWN);
EXPECT_EQ(4, event_handler.num_events_received());
event_handler.ResetEventCounter();
}
WMEvent normal(WM_EVENT_NORMAL);
w1_state->OnWMEvent(&normal);
ASSERT_FALSE(w1_state->IsFullscreen());
{
generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_D,
ui::EF_COMMAND_DOWN);
EXPECT_EQ(3, event_handler.num_events_received());
}
}
TEST_F(SystemShortcutBehaviorTest, AllowSearchBasedPassthrough) {
VoidEventHandler event_handler;
aura::Window* w1 = CreateTestWindowInShell({.window_id = 0});
w1->AddPostTargetHandler(&event_handler);
wm::ActivateWindow(w1);
ui::test::EventGenerator* generator = GetEventGenerator();
user_prefs_->SetManagedPref(
ash::prefs::kSystemShortcutBehavior,
base::Value(static_cast<int>(
SystemShortcutBehaviorType::kAllowSearchBasedPassthrough)));
{
generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_D,
ui::EF_COMMAND_DOWN);
EXPECT_EQ(4, event_handler.num_events_received());
event_handler.ResetEventCounter();
}
user_prefs_->RemoveManagedPref(ash::prefs::kSystemShortcutBehavior);
{
generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_D,
ui::EF_COMMAND_DOWN);
EXPECT_EQ(3, event_handler.num_events_received());
}
}
TEST_F(SystemShortcutBehaviorTest, AllowSearchBasedPassthroughFullscreenOnly) {
VoidEventHandler event_handler;
aura::Window* w1 = CreateTestWindowInShell({.window_id = 0});
w1->AddPostTargetHandler(&event_handler);
wm::ActivateWindow(w1);
ui::test::EventGenerator* generator = GetEventGenerator();
user_prefs_->SetManagedPref(
ash::prefs::kSystemShortcutBehavior,
base::Value(
static_cast<int>(SystemShortcutBehaviorType::
kAllowSearchBasedPassthroughFullscreenOnly)));
{
generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_D,
ui::EF_COMMAND_DOWN);
EXPECT_EQ(3, event_handler.num_events_received());
event_handler.ResetEventCounter();
}
WMEvent fullscreen(WM_EVENT_FULLSCREEN);
WindowState* w1_state = WindowState::Get(w1);
w1_state->OnWMEvent(&fullscreen);
ASSERT_TRUE(w1_state->IsFullscreen());
{
generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_D,
ui::EF_COMMAND_DOWN);
EXPECT_EQ(4, event_handler.num_events_received());
event_handler.ResetEventCounter();
}
WMEvent normal(WM_EVENT_NORMAL);
w1_state->OnWMEvent(&normal);
ASSERT_FALSE(w1_state->IsFullscreen());
{
generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_D,
ui::EF_COMMAND_DOWN);
EXPECT_EQ(3, event_handler.num_events_received());
}
}
class AcceleratorControllerImprovedKeyboardShortcutsTest
: public AcceleratorControllerTest {
public:
AcceleratorControllerImprovedKeyboardShortcutsTest() = default;
~AcceleratorControllerImprovedKeyboardShortcutsTest() override = default;
class TestInputMethodManager : public input_method::MockInputMethodManager {
public:
void AddObserver(
input_method::InputMethodManager::Observer* observer) override {
observers_.AddObserver(observer);
}
void RemoveObserver(
input_method::InputMethodManager::Observer* observer) override {
observers_.RemoveObserver(observer);
}
void NotifyInputMethodChanged() {
for (auto& observer : observers_) {
observer.InputMethodChanged(
this, nullptr, false);
}
}
bool ArePositionalShortcutsUsedByCurrentInputMethod() const override {
return use_positional_shortcuts_;
}
input_method::ImeKeyboard* GetImeKeyboard() override {
return &ime_keyboard_;
}
input_method::FakeImeKeyboard ime_keyboard_;
base::ObserverList<InputMethodManager::Observer>::Unchecked observers_;
bool use_positional_shortcuts_ = false;
};
void SetUp() override {
input_method_manager_ = new TestInputMethodManager();
input_method::InputMethodManager::Initialize(input_method_manager_);
AcceleratorControllerTest::SetUp();
EXPECT_TRUE(
input_method_manager_->observers_.HasObserver(controller_.get()));
}
void TearDown() override {
AcceleratorControllerTest::TearDown();
EXPECT_FALSE(
input_method_manager_->observers_.HasObserver(controller_.get()));
input_method_manager_ = nullptr;
input_method::InputMethodManager::Shutdown();
}
protected:
raw_ptr<TestInputMethodManager> input_method_manager_ =
nullptr;
};
TEST_F(AcceleratorControllerImprovedKeyboardShortcutsTest, InputMethodChanged) {
const ui::Accelerator accelerator = ui::Accelerator(
ui::VKEY_OEM_1, ui::DomCode::BRACKET_LEFT, ui::EF_ALT_DOWN);
input_method_manager_->use_positional_shortcuts_ = false;
input_method_manager_->NotifyInputMethodChanged();
EXPECT_FALSE(controller_->DoesAcceleratorMatchAction(
accelerator, AcceleratorAction::kWindowCycleSnapLeft));
input_method_manager_->use_positional_shortcuts_ = true;
input_method_manager_->NotifyInputMethodChanged();
EXPECT_TRUE(controller_->DoesAcceleratorMatchAction(
accelerator, AcceleratorAction::kWindowCycleSnapLeft));
}
class AcceleratorControllerInputMethodTest : public AcceleratorControllerTest {
public:
AcceleratorControllerInputMethodTest() = default;
~AcceleratorControllerInputMethodTest() override = default;
class AcceleratorMockInputMethod : public ui::MockInputMethod {
public:
AcceleratorMockInputMethod() : ui::MockInputMethod(nullptr) {}
void CancelComposition(const ui::TextInputClient* client) override {
cancel_composition_call_count++;
}
uint32_t cancel_composition_call_count = 0;
};
void SetUp() override {
mock_input_ = new AcceleratorMockInputMethod();
ui::SetUpInputMethodForTesting(mock_input_);
AcceleratorControllerTest::SetUp();
}
void TearDown() override {
mock_input_ = nullptr;
AcceleratorControllerTest::TearDown();
}
protected:
raw_ptr<AcceleratorMockInputMethod> mock_input_ = nullptr;
};
TEST_F(AcceleratorControllerInputMethodTest, AcceleratorClearsComposition) {
EXPECT_EQ(0u, mock_input_->cancel_composition_call_count);
ui::Accelerator unknown_accelerator(ui::VKEY_OEM_MINUS, ui::EF_NONE);
EXPECT_FALSE(controller_->IsRegistered(unknown_accelerator));
EXPECT_FALSE(ProcessInController(unknown_accelerator));
EXPECT_EQ(0u, mock_input_->cancel_composition_call_count);
ui::Accelerator accelerator(ui::VKEY_OEM_MINUS,
ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_TRUE(controller_->IsRegistered(accelerator));
EXPECT_TRUE(ProcessInController(accelerator));
EXPECT_EQ(1u, mock_input_->cancel_composition_call_count);
}
class DeprecatedAcceleratorTester : public AcceleratorControllerTest {
public:
DeprecatedAcceleratorTester() = default;
DeprecatedAcceleratorTester(const DeprecatedAcceleratorTester&) = delete;
DeprecatedAcceleratorTester& operator=(const DeprecatedAcceleratorTester&) =
delete;
~DeprecatedAcceleratorTester() override = default;
ui::Accelerator CreateAccelerator(const AcceleratorData& data) const {
ui::Accelerator result(data.keycode, data.modifiers);
result.set_key_state(data.trigger_on_press
? ui::Accelerator::KeyState::PRESSED
: ui::Accelerator::KeyState::RELEASED);
return result;
}
void ResetStateIfNeeded() {
if (Shell::Get()->session_controller()->IsScreenLocked() ||
Shell::Get()->session_controller()->IsUserSessionBlocked()) {
UnblockUserSession();
}
}
bool ContainsDeprecatedAcceleratorNotification(const char* const id) const {
return nullptr != message_center()->FindVisibleNotificationById(id);
}
bool IsMessageCenterEmpty() const {
return message_center()->GetVisibleNotifications().empty();
}
};
TEST_F(DeprecatedAcceleratorTester, TestDeprecatedAcceleratorsBehavior) {
PrefService* prefs =
Shell::Get()->session_controller()->GetActivePrefService();
ScopedDictPrefUpdate time_update(
prefs, prefs::kDeprecatedAcceleratorNotificationsLastShown);
for (const AcceleratorData& entry : kDeprecatedAccelerators) {
const DeprecatedAcceleratorData* data =
test_api_->GetDeprecatedAcceleratorData(entry.action);
DCHECK(data);
EXPECT_TRUE(IsMessageCenterEmpty());
ui::Accelerator deprecated_accelerator = CreateAccelerator(entry);
time_update->Set(data->pref_name,
base::TimeToValue(base::Time::Now() - base::Hours(24)));
if (data->deprecated_enabled)
EXPECT_TRUE(ProcessInController(deprecated_accelerator));
else
EXPECT_FALSE(ProcessInController(deprecated_accelerator));
EXPECT_TRUE(
ContainsDeprecatedAcceleratorNotification(data->uma_histogram_name));
RemoveAllNotifications();
ResetStateIfNeeded();
}
}
TEST_F(DeprecatedAcceleratorTester, NoNotificationIfReplacementMissing) {
Shell::Get()->ash_accelerator_configuration()->RemoveAccelerator(
AcceleratorAction::kShowShortcutViewer,
ui::Accelerator{ui::VKEY_S, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN});
Shell::Get()->ash_accelerator_configuration()->RemoveAccelerator(
AcceleratorAction::kOpenGetHelp,
ui::Accelerator{ui::VKEY_H, ui::EF_COMMAND_DOWN});
for (const AcceleratorData& entry : kDeprecatedAccelerators) {
const DeprecatedAcceleratorData* data =
test_api_->GetDeprecatedAcceleratorData(entry.action);
DCHECK(data);
EXPECT_TRUE(IsMessageCenterEmpty());
ui::Accelerator deprecated_accelerator = CreateAccelerator(entry);
if (data->deprecated_enabled) {
EXPECT_TRUE(ProcessInController(deprecated_accelerator));
} else {
EXPECT_FALSE(ProcessInController(deprecated_accelerator));
}
EXPECT_FALSE(
ContainsDeprecatedAcceleratorNotification(data->uma_histogram_name));
ResetStateIfNeeded();
}
}
TEST_F(DeprecatedAcceleratorTester, TestNewAccelerators) {
const AcceleratorData kNewAccelerators[] = {
{true, ui::VKEY_L, ui::EF_COMMAND_DOWN, AcceleratorAction::kLockScreen},
{true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
AcceleratorAction::kSwitchToNextIme},
{true, ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN,
AcceleratorAction::kShowTaskManager},
{true, ui::VKEY_K, ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN,
AcceleratorAction::kToggleImeMenuBubble},
{true, ui::VKEY_H, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
AcceleratorAction::kToggleHighContrast},
};
AddTestImes();
EXPECT_TRUE(IsMessageCenterEmpty());
for (auto data : kNewAccelerators) {
EXPECT_TRUE(ProcessInController(CreateAccelerator(data)));
if (data.action != AcceleratorAction::kToggleHighContrast) {
EXPECT_TRUE(IsMessageCenterEmpty());
}
ResetStateIfNeeded();
}
RemoveAllNotifications();
}
using AcceleratorControllerGuestModeTest = NoSessionAshTestBase;
TEST_F(AcceleratorControllerGuestModeTest, IncognitoWindowDisabled) {
SimulateGuestLogin();
EXPECT_FALSE(Shell::Get()->accelerator_controller()->PerformActionIfEnabled(
AcceleratorAction::kNewIncognitoWindow, {}));
}
class MagnifiersAcceleratorsTester : public AcceleratorControllerTest {
public:
MagnifiersAcceleratorsTester() = default;
MagnifiersAcceleratorsTester(const MagnifiersAcceleratorsTester&) = delete;
MagnifiersAcceleratorsTester& operator=(const MagnifiersAcceleratorsTester&) =
delete;
~MagnifiersAcceleratorsTester() override = default;
DockedMagnifierController* docked_magnifier_controller() const {
return Shell::Get()->docked_magnifier_controller();
}
FullscreenMagnifierController* fullscreen_magnifier_controller() const {
return Shell::Get()->fullscreen_magnifier_controller();
}
PrefService* user_pref_service() {
return Shell::Get()->session_controller()->GetUserPrefServiceForUser(
AccountId::FromUserEmail(kUserEmail));
}
void SetUp() override {
AcceleratorControllerTest::SetUp();
SimulateUserLogin({kUserEmail});
}
};
class FakeMagnificationManager {
public:
FakeMagnificationManager() = default;
FakeMagnificationManager(const FakeMagnificationManager&) = delete;
FakeMagnificationManager& operator=(const FakeMagnificationManager&) = delete;
void SetPrefs(PrefService* prefs) {
pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
pref_change_registrar_->Init(prefs);
pref_change_registrar_->Add(
prefs::kAccessibilityScreenMagnifierEnabled,
base::BindRepeating(&FakeMagnificationManager::UpdateMagnifierFromPrefs,
base::Unretained(this)));
prefs_ = prefs;
}
void UpdateMagnifierFromPrefs() {
Shell::Get()->fullscreen_magnifier_controller()->SetEnabled(
prefs_->GetBoolean(prefs::kAccessibilityScreenMagnifierEnabled));
}
private:
std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
raw_ptr<PrefService> prefs_;
};
TEST_F(MagnifiersAcceleratorsTester, TestToggleFullscreenMagnifier) {
FakeMagnificationManager manager;
manager.SetPrefs(user_pref_service());
EXPECT_FALSE(docked_magnifier_controller()->GetEnabled());
EXPECT_FALSE(fullscreen_magnifier_controller()->IsEnabled());
EXPECT_FALSE(IsConfirmationDialogOpen());
AccessibilityController* accessibility_controller =
Shell::Get()->accessibility_controller();
const ui::Accelerator fullscreen_magnifier_accelerator(
ui::VKEY_M, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN);
EXPECT_FALSE(
accessibility_controller->fullscreen_magnifier().WasDialogAccepted());
EXPECT_TRUE(ProcessInController(fullscreen_magnifier_accelerator));
EXPECT_TRUE(IsConfirmationDialogOpen());
EXPECT_TRUE(fullscreen_magnifier_controller()->IsEnabled());
EXPECT_FALSE(docked_magnifier_controller()->GetEnabled());
CancelConfirmationDialog();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(
accessibility_controller->fullscreen_magnifier().WasDialogAccepted());
EXPECT_FALSE(fullscreen_magnifier_controller()->IsEnabled());
EXPECT_TRUE(ProcessInController(fullscreen_magnifier_accelerator));
EXPECT_TRUE(IsConfirmationDialogOpen());
EXPECT_TRUE(fullscreen_magnifier_controller()->IsEnabled());
AcceptConfirmationDialog();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(IsConfirmationDialogOpen());
EXPECT_FALSE(docked_magnifier_controller()->GetEnabled());
EXPECT_TRUE(fullscreen_magnifier_controller()->IsEnabled());
EXPECT_TRUE(ContainsFullscreenMagnifierNotification());
EXPECT_FALSE(
IsNotificationPinned(kFullscreenMagnifierToggleAccelNotificationId));
EXPECT_TRUE(ProcessInController(fullscreen_magnifier_accelerator));
EXPECT_FALSE(docked_magnifier_controller()->GetEnabled());
EXPECT_FALSE(fullscreen_magnifier_controller()->IsEnabled());
EXPECT_TRUE(
accessibility_controller->fullscreen_magnifier().WasDialogAccepted());
EXPECT_FALSE(IsConfirmationDialogOpen());
EXPECT_FALSE(ContainsFullscreenMagnifierNotification());
EXPECT_TRUE(ProcessInController(fullscreen_magnifier_accelerator));
EXPECT_FALSE(IsConfirmationDialogOpen());
EXPECT_TRUE(
accessibility_controller->fullscreen_magnifier().WasDialogAccepted());
EXPECT_FALSE(docked_magnifier_controller()->GetEnabled());
EXPECT_TRUE(fullscreen_magnifier_controller()->IsEnabled());
EXPECT_TRUE(ContainsFullscreenMagnifierNotification());
RemoveAllNotifications();
}
TEST_F(MagnifiersAcceleratorsTester, TestToggleDockedMagnifier) {
EXPECT_FALSE(docked_magnifier_controller()->GetEnabled());
EXPECT_FALSE(fullscreen_magnifier_controller()->IsEnabled());
EXPECT_FALSE(IsConfirmationDialogOpen());
AccessibilityController* accessibility_controller =
Shell::Get()->accessibility_controller();
const ui::Accelerator docked_magnifier_accelerator(
ui::VKEY_D, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN);
EXPECT_TRUE(ProcessInController(docked_magnifier_accelerator));
EXPECT_TRUE(IsConfirmationDialogOpen());
AcceptConfirmationDialog();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(IsConfirmationDialogOpen());
EXPECT_TRUE(docked_magnifier_controller()->GetEnabled());
EXPECT_FALSE(fullscreen_magnifier_controller()->IsEnabled());
EXPECT_TRUE(ContainsDockedMagnifierNotification());
EXPECT_FALSE(IsNotificationPinned(kDockedMagnifierToggleAccelNotificationId));
EXPECT_TRUE(ProcessInController(docked_magnifier_accelerator));
EXPECT_FALSE(docked_magnifier_controller()->GetEnabled());
EXPECT_FALSE(fullscreen_magnifier_controller()->IsEnabled());
EXPECT_TRUE(accessibility_controller->docked_magnifier().WasDialogAccepted());
EXPECT_FALSE(IsConfirmationDialogOpen());
EXPECT_FALSE(ContainsDockedMagnifierNotification());
EXPECT_TRUE(ProcessInController(docked_magnifier_accelerator));
EXPECT_FALSE(IsConfirmationDialogOpen());
EXPECT_TRUE(docked_magnifier_controller()->GetEnabled());
EXPECT_FALSE(fullscreen_magnifier_controller()->IsEnabled());
EXPECT_TRUE(ContainsDockedMagnifierNotification());
RemoveAllNotifications();
}
class AccessibilityAcceleratorTester : public MagnifiersAcceleratorsTester {
public:
AccessibilityAcceleratorTester() = default;
~AccessibilityAcceleratorTester() override = default;
AccessibilityAcceleratorTester(const AccessibilityAcceleratorTester&) =
delete;
AccessibilityAcceleratorTester& operator=(
const AccessibilityAcceleratorTester&) = delete;
bool ContainsAccessibilityNotification(
const std::string& notification_id) const {
return nullptr !=
message_center()->FindVisibleNotificationById(notification_id);
}
void TestAccessibilityAcceleratorControlledByPref(
const std::string& pref_name,
const char* notification_id,
const ui::Accelerator& accelerator) {
base::HistogramTester histogram_tester_;
EXPECT_FALSE(user_pref_service()->GetBoolean(pref_name));
EXPECT_TRUE(
user_pref_service()->GetBoolean(prefs::kAccessibilityShortcutsEnabled));
EXPECT_FALSE(IsConfirmationDialogOpen());
if (notification_id)
EXPECT_FALSE(ContainsAccessibilityNotification(notification_id));
user_pref_service()->SetBoolean(prefs::kAccessibilityShortcutsEnabled,
false);
EXPECT_TRUE(ProcessInController(accelerator));
EXPECT_FALSE(IsConfirmationDialogOpen());
if (notification_id)
EXPECT_TRUE(ContainsAccessibilityNotification(notification_id));
EXPECT_FALSE(user_pref_service()->GetBoolean(pref_name));
user_pref_service()->SetBoolean(prefs::kAccessibilityShortcutsEnabled,
true);
EXPECT_TRUE(ProcessInController(accelerator));
if (notification_id)
AcceptConfirmationDialog();
base::RunLoop().RunUntilIdle();
message_center::NotificationList::Notifications notifications =
message_center()->GetVisibleNotifications();
ASSERT_EQ(1u, notifications.size());
EXPECT_TRUE(user_pref_service()->GetBoolean(pref_name));
if (notification_id)
EXPECT_TRUE(ContainsAccessibilityNotification(notification_id));
EXPECT_TRUE(ProcessInController(accelerator));
if (notification_id)
EXPECT_FALSE(ContainsAccessibilityNotification(notification_id));
EXPECT_FALSE(user_pref_service()->GetBoolean(pref_name));
RemoveAllNotifications();
}
};
TEST_F(AccessibilityAcceleratorTester, DisableAccessibilityAccelerators) {
FakeMagnificationManager manager;
manager.SetPrefs(user_pref_service());
for (const auto& test_data : kAccessibilityAcceleratorMap) {
TestAccessibilityAcceleratorControlledByPref(
test_data.pref_name, test_data.notification_id, test_data.accelerator);
}
}
TEST_F(AccessibilityAcceleratorTester,
DisableScreenCaptureAcceleratorsIfSessionIsActive) {
auto* controller = CaptureModeController::Get();
EXPECT_FALSE(controller->IsActive());
EXPECT_TRUE(ProcessInController(ui::Accelerator(
ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN)));
EXPECT_TRUE(controller->IsActive());
EXPECT_EQ(CaptureModeSource::kWindow, controller->source());
EXPECT_TRUE(ProcessInController(ui::Accelerator(
ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN)));
EXPECT_EQ(CaptureModeSource::kWindow, controller->source());
controller->Stop();
EXPECT_TRUE(ProcessInController(ui::Accelerator(
ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN)));
EXPECT_TRUE(controller->IsActive());
EXPECT_EQ(CaptureModeSource::kRegion, controller->source());
EXPECT_TRUE(ProcessInController(ui::Accelerator(
ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN)));
EXPECT_EQ(CaptureModeSource::kRegion, controller->source());
}
struct MediaSessionAcceleratorTestConfig {
bool service_enabled;
std::optional<MediaSessionAction> with_action_enabled;
bool eligible_action = false;
bool force_key_handling = false;
};
}
class MediaSessionAcceleratorTest
: public AcceleratorControllerTest,
public testing::WithParamInterface<MediaSessionAcceleratorTestConfig> {
public:
MediaSessionAcceleratorTest() = default;
MediaSessionAcceleratorTest(const MediaSessionAcceleratorTest&) = delete;
MediaSessionAcceleratorTest& operator=(const MediaSessionAcceleratorTest&) =
delete;
~MediaSessionAcceleratorTest() override = default;
void SetUp() override {
if (service_enabled()) {
scoped_feature_list_.InitAndEnableFeature(
media::kHardwareMediaKeyHandling);
} else {
scoped_feature_list_.InitAndDisableFeature(
media::kHardwareMediaKeyHandling);
}
AcceleratorControllerTest::SetUp();
client_ = std::make_unique<TestMediaClient>();
controller_ = std::make_unique<media_session::test::TestMediaController>();
MediaControllerImpl* media_controller = Shell::Get()->media_controller();
media_controller->SetClient(client_.get());
media_controller->SetMediaSessionControllerForTest(
controller_->CreateMediaControllerRemote());
media_controller->SetForceMediaClientKeyHandling(
GetParam().force_key_handling);
media_controller->FlushForTesting();
}
void MaybeEnableMediaSession(
media_session::mojom::MediaPlaybackState playback_state) {
if (!GetParam().with_action_enabled)
return;
SimulateActionsChanged(GetParam().with_action_enabled);
SimulatePlaybackState(playback_state);
}
void SimulateActionsChanged(std::optional<MediaSessionAction> action) {
std::vector<MediaSessionAction> actions;
if (action)
actions.push_back(*action);
controller()->SimulateMediaSessionActionsChanged(actions);
controller()->Flush();
}
TestMediaClient* client() const { return client_.get(); }
media_session::test::TestMediaController* controller() const {
return controller_.get();
}
bool service_enabled() const { return GetParam().service_enabled; }
bool eligible_action() const { return GetParam().eligible_action; }
bool force_key_handling() const { return GetParam().force_key_handling; }
void ExpectActionRecorded(ui::MediaHardwareKeyAction action) {
histogram_tester_.ExpectBucketCount(
ui::kMediaHardwareKeyActionHistogramName,
static_cast<base::HistogramBase::Sample32>(action), 1);
}
private:
void SimulatePlaybackState(
media_session::mojom::MediaPlaybackState playback_state) {
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->state =
media_session::mojom::MediaSessionInfo::SessionState::kActive;
session_info->playback_state = playback_state;
controller()->SimulateMediaSessionInfoChanged(std::move(session_info));
controller()->Flush();
}
std::unique_ptr<TestMediaClient> client_;
std::unique_ptr<media_session::test::TestMediaController> controller_;
base::HistogramTester histogram_tester_;
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(
All,
MediaSessionAcceleratorTest,
testing::Values(
MediaSessionAcceleratorTestConfig{true, MediaSessionAction::kPlay,
true},
MediaSessionAcceleratorTestConfig{true, MediaSessionAction::kPause,
true},
MediaSessionAcceleratorTestConfig{
true, MediaSessionAction::kPreviousTrack, true},
MediaSessionAcceleratorTestConfig{true, MediaSessionAction::kNextTrack,
true},
MediaSessionAcceleratorTestConfig{true,
MediaSessionAction::kSeekBackward},
MediaSessionAcceleratorTestConfig{true,
MediaSessionAction::kSeekForward},
MediaSessionAcceleratorTestConfig{true, MediaSessionAction::kStop},
MediaSessionAcceleratorTestConfig{false, MediaSessionAction::kPlay},
MediaSessionAcceleratorTestConfig{false, MediaSessionAction::kPause},
MediaSessionAcceleratorTestConfig{false,
MediaSessionAction::kPreviousTrack},
MediaSessionAcceleratorTestConfig{false,
MediaSessionAction::kNextTrack},
MediaSessionAcceleratorTestConfig{false,
MediaSessionAction::kSeekBackward},
MediaSessionAcceleratorTestConfig{false,
MediaSessionAction::kSeekForward},
MediaSessionAcceleratorTestConfig{false, MediaSessionAction::kStop},
MediaSessionAcceleratorTestConfig{true, MediaSessionAction::kPlay,
false, true},
MediaSessionAcceleratorTestConfig{true, MediaSessionAction::kPause,
false, true},
MediaSessionAcceleratorTestConfig{true, MediaSessionAction::kNextTrack,
false, true},
MediaSessionAcceleratorTestConfig{
true, MediaSessionAction::kPreviousTrack, false, true}));
TEST_P(MediaSessionAcceleratorTest, MediaPlaybackAcceleratorsBehavior) {
const ui::KeyboardCode media_keys[] = {ui::VKEY_MEDIA_NEXT_TRACK,
ui::VKEY_MEDIA_PLAY_PAUSE,
ui::VKEY_MEDIA_PREV_TRACK};
::wm::AcceleratorFilter filter(
std::make_unique<PreTargetAcceleratorHandler>());
for (ui::KeyboardCode key : media_keys) {
std::unique_ptr<aura::Window> window(
CreateTestWindowInShell({.window_id = 1}));
{
ui::KeyEvent press_key(ui::EventType::kKeyPressed, key, ui::EF_NONE);
ui::Event::DispatcherApi dispatch_helper(&press_key);
dispatch_helper.set_target(window.get());
filter.OnKeyEvent(&press_key);
EXPECT_EQ(service_enabled(), press_key.stopped_propagation());
}
WindowState::Get(window.get())->SetCanConsumeSystemKeys(true);
{
ui::KeyEvent press_key(ui::EventType::kKeyPressed, key, ui::EF_NONE);
ui::Event::DispatcherApi dispatch_helper(&press_key);
dispatch_helper.set_target(window.get());
filter.OnKeyEvent(&press_key);
EXPECT_FALSE(press_key.stopped_propagation());
}
}
}
TEST_P(MediaSessionAcceleratorTest, MediaGlobalAccelerators_NextTrack) {
MaybeEnableMediaSession(media_session::mojom::MediaPlaybackState::kPaused);
EXPECT_EQ(0, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
ProcessInController(ui::Accelerator(ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE));
Shell::Get()->media_controller()->FlushForTesting();
if (service_enabled() && eligible_action() && !force_key_handling()) {
EXPECT_EQ(0, client()->handle_media_next_track_count());
EXPECT_EQ(1, controller()->next_track_count());
} else {
EXPECT_EQ(1, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
}
ExpectActionRecorded(ui::MediaHardwareKeyAction::kNextTrack);
}
TEST_P(MediaSessionAcceleratorTest, MediaGlobalAccelerators_Play) {
MaybeEnableMediaSession(media_session::mojom::MediaPlaybackState::kPaused);
EXPECT_EQ(0, client()->handle_media_play_pause_count());
EXPECT_EQ(0, controller()->resume_count());
ProcessInController(ui::Accelerator(ui::VKEY_MEDIA_PLAY_PAUSE, ui::EF_NONE));
Shell::Get()->media_controller()->FlushForTesting();
if (service_enabled() && eligible_action()) {
EXPECT_EQ(0, client()->handle_media_play_pause_count());
EXPECT_EQ(1, controller()->resume_count());
ExpectActionRecorded(ui::MediaHardwareKeyAction::kPlay);
} else {
EXPECT_EQ(1, client()->handle_media_play_pause_count());
EXPECT_EQ(0, controller()->resume_count());
ExpectActionRecorded(ui::MediaHardwareKeyAction::kPlayPause);
}
}
TEST_P(MediaSessionAcceleratorTest, MediaGlobalAccelerators_Pause) {
MaybeEnableMediaSession(media_session::mojom::MediaPlaybackState::kPlaying);
EXPECT_EQ(0, client()->handle_media_play_pause_count());
EXPECT_EQ(0, controller()->suspend_count());
ProcessInController(ui::Accelerator(ui::VKEY_MEDIA_PLAY_PAUSE, ui::EF_NONE));
Shell::Get()->media_controller()->FlushForTesting();
if (service_enabled() && eligible_action() && !force_key_handling()) {
EXPECT_EQ(0, client()->handle_media_play_pause_count());
EXPECT_EQ(1, controller()->suspend_count());
ExpectActionRecorded(ui::MediaHardwareKeyAction::kPause);
} else {
EXPECT_EQ(1, client()->handle_media_play_pause_count());
EXPECT_EQ(0, controller()->suspend_count());
ExpectActionRecorded(ui::MediaHardwareKeyAction::kPlayPause);
}
}
TEST_P(MediaSessionAcceleratorTest, MediaGlobalAccelerators_PrevTrack) {
MaybeEnableMediaSession(media_session::mojom::MediaPlaybackState::kPaused);
EXPECT_EQ(0, client()->handle_media_prev_track_count());
EXPECT_EQ(0, controller()->previous_track_count());
ProcessInController(ui::Accelerator(ui::VKEY_MEDIA_PREV_TRACK, ui::EF_NONE));
Shell::Get()->media_controller()->FlushForTesting();
if (service_enabled() && eligible_action() && !force_key_handling()) {
EXPECT_EQ(0, client()->handle_media_prev_track_count());
EXPECT_EQ(1, controller()->previous_track_count());
} else {
EXPECT_EQ(1, client()->handle_media_prev_track_count());
EXPECT_EQ(0, controller()->previous_track_count());
}
ExpectActionRecorded(ui::MediaHardwareKeyAction::kPreviousTrack);
}
TEST_P(MediaSessionAcceleratorTest,
MediaGlobalAccelerators_UpdateAction_Disable) {
MaybeEnableMediaSession(media_session::mojom::MediaPlaybackState::kPaused);
EXPECT_EQ(0, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
ProcessInController(ui::Accelerator(ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE));
Shell::Get()->media_controller()->FlushForTesting();
if (service_enabled() && eligible_action() && !force_key_handling()) {
EXPECT_EQ(0, client()->handle_media_next_track_count());
EXPECT_EQ(1, controller()->next_track_count());
} else {
EXPECT_EQ(1, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
}
SimulateActionsChanged(std::nullopt);
ProcessInController(ui::Accelerator(ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE));
Shell::Get()->media_controller()->FlushForTesting();
if (service_enabled() && eligible_action() && !force_key_handling()) {
EXPECT_EQ(1, client()->handle_media_next_track_count());
EXPECT_EQ(1, controller()->next_track_count());
} else {
EXPECT_EQ(2, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
}
}
TEST_P(MediaSessionAcceleratorTest,
MediaGlobalAccelerators_UpdateAction_Enable) {
EXPECT_EQ(0, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
ProcessInController(ui::Accelerator(ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE));
Shell::Get()->media_controller()->FlushForTesting();
EXPECT_EQ(1, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
MaybeEnableMediaSession(media_session::mojom::MediaPlaybackState::kPaused);
ProcessInController(ui::Accelerator(ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE));
Shell::Get()->media_controller()->FlushForTesting();
if (service_enabled() && eligible_action() && !force_key_handling()) {
EXPECT_EQ(1, client()->handle_media_next_track_count());
EXPECT_EQ(1, controller()->next_track_count());
} else {
EXPECT_EQ(2, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
}
}
TEST_P(MediaSessionAcceleratorTest,
MediaGlobalAccelerators_UpdateForceKeyHandling) {
MaybeEnableMediaSession(media_session::mojom::MediaPlaybackState::kPaused);
EXPECT_EQ(0, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
ProcessInController(ui::Accelerator(ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE));
Shell::Get()->media_controller()->FlushForTesting();
if (service_enabled() && eligible_action() && !force_key_handling()) {
EXPECT_EQ(0, client()->handle_media_next_track_count());
EXPECT_EQ(1, controller()->next_track_count());
} else {
EXPECT_EQ(1, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
}
Shell::Get()->media_controller()->SetForceMediaClientKeyHandling(false);
ProcessInController(ui::Accelerator(ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE));
Shell::Get()->media_controller()->FlushForTesting();
if (service_enabled() && force_key_handling()) {
EXPECT_EQ(1, client()->handle_media_next_track_count());
EXPECT_EQ(1, controller()->next_track_count());
} else if (service_enabled() && eligible_action()) {
EXPECT_EQ(0, client()->handle_media_next_track_count());
EXPECT_EQ(2, controller()->next_track_count());
} else {
EXPECT_EQ(2, client()->handle_media_next_track_count());
EXPECT_EQ(0, controller()->next_track_count());
}
}
TEST_F(AcceleratorControllerTest, ToggleGameDashboardAccelerator) {
const ui::Accelerator accelerator(ui::VKEY_G, ui::EF_COMMAND_DOWN);
EXPECT_FALSE(ProcessInController(accelerator));
std::unique_ptr<aura::Window> window =
CreateAppWindow(gfx::Rect(5, 5, 20, 20), chromeos::AppType::ARC_APP);
window->SetProperty(kAppIDKey,
std::string(TestGameDashboardDelegate::kGameAppId));
EXPECT_FALSE(ProcessInController(accelerator));
window->SetProperty(kArcGameControlsFlagsKey, ArcGameControlsFlag::kKnown);
EXPECT_TRUE(ProcessInController(accelerator));
window->SetProperty(
kArcGameControlsFlagsKey,
static_cast<ArcGameControlsFlag>(ArcGameControlsFlag::kKnown |
ArcGameControlsFlag::kEdit));
EXPECT_FALSE(ProcessInController(accelerator));
window = CreateAppWindow(gfx::Rect(5, 5, 20, 20), chromeos::AppType::BROWSER);
window->SetProperty(
kAppIDKey, std::string(TestGameDashboardDelegate::kAllowlistedAppId));
EXPECT_TRUE(ProcessInController(accelerator));
}
}