blob: cdcf3bce5b52131f7e3ed65a89218e9ca3b5da58 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <vector>
#include "ash/accessibility/sticky_keys/sticky_keys_controller.h"
#include "ash/accessibility/sticky_keys/sticky_keys_overlay.h"
#include "ash/constants/ash_features.h"
#include "ash/shell.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ash/events/event_rewriter_delegate_impl.h"
#include "chrome/browser/ash/input_method/input_method_configuration.h"
#include "chrome/browser/ash/input_method/mock_input_method_manager_impl.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/notifications/deprecation_notification_controller.h"
#include "chrome/browser/chromeos/preferences.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_ash_test_base.h"
#include "components/prefs/pref_member.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_manager/scoped_user_manager.h"
#include "device/udev_linux/fake_udev_loader.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/ime/ash/fake_ime_keyboard.h"
#include "ui/base/ui_base_features.h"
#include "ui/chromeos/events/event_rewriter_chromeos.h"
#include "ui/chromeos/events/modifier_key.h"
#include "ui/chromeos/events/pref_names.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/device_data_manager_test_api.h"
#include "ui/events/event.h"
#include "ui/events/event_rewriter.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/test/events_test_utils.h"
#include "ui/events/test/test_event_processor.h"
#include "ui/events/test/test_event_rewriter_continuation.h"
#include "ui/message_center/fake_message_center.h"
#include "ui/wm/core/window_util.h"
namespace {
constexpr int kKeyboardDeviceId = 123;
constexpr uint32_t kNoScanCode = 0;
constexpr char kKbdSysPath[] = "/devices/platform/i8042/serio2/input/input1";
constexpr char kKbdTopRowPropertyName[] = "CROS_KEYBOARD_TOP_ROW_LAYOUT";
constexpr char kKbdTopRowLayoutAttributeName[] = "function_row_physmap";
constexpr char kKbdTopRowLayoutUnspecified[] = "";
constexpr char kKbdTopRowLayout1Tag[] = "1";
constexpr char kKbdTopRowLayout2Tag[] = "2";
constexpr char kKbdTopRowLayoutWilcoTag[] = "3";
constexpr char kKbdTopRowLayoutDrallionTag[] = "4";
// A default example of the layout string read from the function_row_physmap
// sysfs attribute. The values represent the scan codes for each position
// in the top row, which maps to F-Keys.
constexpr char kKbdDefaultCustomTopRowLayout[] =
"01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f";
class TestEventRewriterContinuation
: public ui::test::TestEventRewriterContinuation {
public:
TestEventRewriterContinuation() = default;
~TestEventRewriterContinuation() override = default;
TestEventRewriterContinuation(const TestEventRewriterContinuation&) = delete;
TestEventRewriterContinuation& operator=(
const TestEventRewriterContinuation&) = delete;
ui::EventDispatchDetails SendEvent(const ui::Event* event) override {
passthrough_events.push_back(ui::Event::Clone(*event));
return ui::EventDispatchDetails();
}
ui::EventDispatchDetails SendEventFinally(const ui::Event* event) override {
rewritten_events.push_back(ui::Event::Clone(*event));
return ui::EventDispatchDetails();
}
ui::EventDispatchDetails DiscardEvent() override {
return ui::EventDispatchDetails();
}
std::vector<std::unique_ptr<ui::Event>> rewritten_events;
std::vector<std::unique_ptr<ui::Event>> passthrough_events;
base::WeakPtrFactory<TestEventRewriterContinuation> weak_ptr_factory_{this};
};
std::string GetExpectedResultAsString(ui::EventType ui_type,
ui::KeyboardCode ui_keycode,
ui::DomCode code,
int ui_flags, // ui::EventFlags
ui::DomKey key,
uint32_t scan_code) {
return base::StringPrintf(
"type=%d code=0x%06X flags=0x%X vk=0x%02X key=0x%08X scan=0x%08X",
ui_type, static_cast<unsigned int>(code), ui_flags & ~ui::EF_IS_REPEAT,
ui_keycode, static_cast<unsigned int>(key), scan_code);
}
std::string GetKeyEventAsString(const ui::KeyEvent& keyevent) {
return GetExpectedResultAsString(keyevent.type(), keyevent.key_code(),
keyevent.code(), keyevent.flags(),
keyevent.GetDomKey(), keyevent.scan_code());
}
std::string GetRewrittenEventAsString(ui::EventRewriter* const rewriter,
ui::EventType ui_type,
ui::KeyboardCode ui_keycode,
ui::DomCode code,
int ui_flags, // ui::EventFlags
ui::DomKey key,
uint32_t scan_code,
int device_id = kKeyboardDeviceId) {
ui::KeyEvent event(ui_type, ui_keycode, code, ui_flags, key,
ui::EventTimeForNow());
event.set_scan_code(scan_code);
event.set_source_device_id(device_id);
TestEventRewriterContinuation continuation;
rewriter->RewriteEvent(event, continuation.weak_ptr_factory_.GetWeakPtr());
if (!continuation.rewritten_events.empty())
return GetKeyEventAsString(*continuation.rewritten_events[0]->AsKeyEvent());
return GetKeyEventAsString(event);
}
// Table entry for simple single key event rewriting tests.
struct KeyTestCase {
ui::EventType type;
struct Event {
ui::KeyboardCode key_code;
ui::DomCode code;
int flags; // ui::EventFlags
ui::DomKey::Base key;
uint32_t scan_code = kNoScanCode;
} input, expected;
int device_id = kKeyboardDeviceId;
bool triggers_notification = false;
};
std::string GetTestCaseAsString(ui::EventType ui_type,
const KeyTestCase::Event& test) {
return GetExpectedResultAsString(ui_type, test.key_code, test.code,
test.flags, test.key, test.scan_code);
}
// Tests a single stateless key rewrite operation.
void CheckKeyTestCase(ui::EventRewriter* const rewriter,
const KeyTestCase& test) {
SCOPED_TRACE("\nSource: " + GetTestCaseAsString(test.type, test.input));
EXPECT_EQ(GetTestCaseAsString(test.type, test.expected),
GetRewrittenEventAsString(rewriter, test.type, test.input.key_code,
test.input.code, test.input.flags,
test.input.key, test.input.scan_code,
test.device_id));
}
} // namespace
namespace ash {
class EventRewriterTest : public ChromeAshTestBase {
public:
EventRewriterTest()
: fake_user_manager_(new FakeChromeUserManager),
user_manager_enabler_(base::WrapUnique(fake_user_manager_)) {}
~EventRewriterTest() override {}
void SetUp() override {
input_method_manager_mock_ = new input_method::MockInputMethodManagerImpl;
input_method::InitializeForTesting(
input_method_manager_mock_); // pass ownership
auto deprecation_controller =
std::make_unique<DeprecationNotificationController>(&message_center_);
deprecation_controller_ = deprecation_controller.get();
delegate_ = std::make_unique<EventRewriterDelegateImpl>(
nullptr, std::move(deprecation_controller));
delegate_->set_pref_service_for_testing(prefs());
device_data_manager_test_api_.SetKeyboardDevices({});
rewriter_ = std::make_unique<ui::EventRewriterChromeOS>(
delegate_.get(), nullptr, false, &fake_ime_keyboard_);
ChromeAshTestBase::SetUp();
}
void TearDown() override {
ChromeAshTestBase::TearDown();
// Shutdown() deletes the IME mock object.
input_method::Shutdown();
}
ui::EventRewriter* rewriter() { return rewriter_.get(); }
protected:
void TestRewriteNumPadKeys();
void TestRewriteNumPadKeysOnAppleKeyboard();
// Parameterized version of test depending on feature flag values. The feature
// kUseSearchClickForRightClick determines if this should test for alt-click
// or search-click.
void DontRewriteIfNotRewritten(int right_click_flags);
ui::MouseEvent RewriteMouseButtonEvent(const ui::MouseEvent& event) {
TestEventRewriterContinuation continuation;
rewriter_->RewriteMouseButtonEventForTesting(
event, continuation.weak_ptr_factory_.GetWeakPtr());
if (!continuation.rewritten_events.empty())
return ui::MouseEvent(*continuation.rewritten_events[0]->AsMouseEvent());
return ui::MouseEvent(event);
}
sync_preferences::TestingPrefServiceSyncable* prefs() { return &prefs_; }
void InitModifierKeyPref(IntegerPrefMember* int_pref,
const std::string& pref_name,
ui::chromeos::ModifierKey modifierKey) {
if (int_pref->GetPrefName() != pref_name) // skip if already initialized.
int_pref->Init(pref_name, prefs());
int_pref->SetValue(static_cast<int>(modifierKey));
}
void SetupKeyboard(const std::string& name,
const std::string& layout = "",
ui::InputDeviceType type = ui::INPUT_DEVICE_INTERNAL,
bool has_custom_top_row = false) {
// Add a fake device to udev.
const ui::InputDevice keyboard(kKeyboardDeviceId, type, name, /*phys=*/"",
base::FilePath(kKbdSysPath), /*vendor=*/-1,
/*product=*/-1, /*version=*/-1);
// Old CrOS keyboards supply an integer/enum as a sysfs property to identify
// their layout type. New keyboards provide the mapping of scan codes to
// F-Key position via an attribute.
std::map<std::string, std::string> sysfs_properties;
std::map<std::string, std::string> sysfs_attributes;
if (has_custom_top_row) {
if (!layout.empty())
sysfs_attributes[kKbdTopRowLayoutAttributeName] = layout;
} else {
if (!layout.empty())
sysfs_properties[kKbdTopRowPropertyName] = layout;
}
fake_udev_.Reset();
fake_udev_.AddFakeDevice(keyboard.name, keyboard.sys_path.value(),
/*subsystem=*/"input", /*devnode=*/absl::nullopt,
/*devtype=*/absl::nullopt,
std::move(sysfs_attributes),
std::move(sysfs_properties));
// Reset the state of the device manager.
device_data_manager_test_api_.SetKeyboardDevices({});
device_data_manager_test_api_.SetKeyboardDevices({keyboard});
// Reset the state of the EventRewriter.
rewriter_->ResetStateForTesting();
rewriter_->KeyboardDeviceAddedForTesting(kKeyboardDeviceId);
rewriter_->set_last_keyboard_device_id_for_testing(kKeyboardDeviceId);
}
void TestKeyboard(const std::string& name,
const std::string& layout,
ui::InputDeviceType type,
bool has_custom_top_row,
const std::vector<KeyTestCase>& tests) {
SetupKeyboard(name, layout, type, has_custom_top_row);
for (const auto& test : tests) {
CheckKeyTestCase(rewriter(), test);
const size_t expected_notification_count =
test.triggers_notification ? 1 : 0;
EXPECT_EQ(message_center_.NotificationCount(),
expected_notification_count);
ClearNotifications();
}
}
void TestInternalChromeKeyboard(const std::vector<KeyTestCase>& tests) {
TestKeyboard("Internal Keyboard", kKbdTopRowLayoutUnspecified,
ui::INPUT_DEVICE_INTERNAL, /*has_custom_top_row=*/false,
tests);
}
void TestInternalChromeCustomLayoutKeyboard(
const std::vector<KeyTestCase>& tests) {
TestKeyboard("Internal Custom Layout Keyboard",
kKbdDefaultCustomTopRowLayout, ui::INPUT_DEVICE_INTERNAL,
/*has_custom_top_row=*/true, tests);
}
void TestExternalChromeKeyboard(const std::vector<KeyTestCase>& tests) {
TestKeyboard("External Chrome Keyboard", kKbdTopRowLayout1Tag,
ui::INPUT_DEVICE_UNKNOWN, /*has_custom_top_row=*/false, tests);
}
void TestExternalChromeCustomLayoutKeyboard(
const std::vector<KeyTestCase>& tests) {
TestKeyboard("External Chrome Custom Layout Keyboard",
kKbdDefaultCustomTopRowLayout, ui::INPUT_DEVICE_UNKNOWN,
/*has_custom_top_row=*/true, tests);
}
void TestExternalGenericKeyboard(const std::vector<KeyTestCase>& tests) {
TestKeyboard("PC Keyboard", kKbdTopRowLayoutUnspecified,
ui::INPUT_DEVICE_UNKNOWN, /*has_custom_top_row=*/false, tests);
}
void TestExternalAppleKeyboard(const std::vector<KeyTestCase>& tests) {
TestKeyboard("Apple Keyboard", kKbdTopRowLayoutUnspecified,
ui::INPUT_DEVICE_UNKNOWN, /*has_custom_top_row=*/false, tests);
}
void TestChromeKeyboardVariants(const std::vector<KeyTestCase>& tests) {
TestInternalChromeKeyboard(tests);
TestExternalChromeKeyboard(tests);
}
void TestChromeCustomLayoutKeyboardVariants(
const std::vector<KeyTestCase>& tests) {
TestInternalChromeCustomLayoutKeyboard(tests);
TestExternalChromeCustomLayoutKeyboard(tests);
}
void TestNonAppleKeyboardVariants(const std::vector<KeyTestCase>& tests) {
TestChromeKeyboardVariants(tests);
TestChromeCustomLayoutKeyboardVariants(tests);
TestExternalGenericKeyboard(tests);
}
void TestNonAppleNonCustomLayoutKeyboardVariants(
const std::vector<KeyTestCase>& tests) {
TestChromeKeyboardVariants(tests);
TestExternalGenericKeyboard(tests);
}
void TestAllKeyboardVariants(const std::vector<KeyTestCase>& tests) {
TestNonAppleKeyboardVariants(tests);
TestExternalAppleKeyboard(tests);
}
void ClearNotifications() {
message_center_.RemoveAllNotifications(
false, message_center::FakeMessageCenter::RemoveType::ALL);
deprecation_controller_->ResetStateForTesting();
}
base::test::ScopedFeatureList scoped_feature_list_;
FakeChromeUserManager* fake_user_manager_; // Not owned.
user_manager::ScopedUserManager user_manager_enabler_;
input_method::MockInputMethodManagerImpl* input_method_manager_mock_;
testing::FakeUdevLoader fake_udev_;
ui::DeviceDataManagerTestApi device_data_manager_test_api_;
sync_preferences::TestingPrefServiceSyncable prefs_;
std::unique_ptr<EventRewriterDelegateImpl> delegate_;
chromeos::input_method::FakeImeKeyboard fake_ime_keyboard_;
std::unique_ptr<ui::EventRewriterChromeOS> rewriter_;
DeprecationNotificationController* deprecation_controller_; // Not owned.
message_center::FakeMessageCenter message_center_;
};
TEST_F(EventRewriterTest, TestRewriteCommandToControl) {
// First, test non Apple keyboards, they should all behave the same.
TestNonAppleKeyboardVariants({
// VKEY_A, Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN,
ui::DomKey::UNIDENTIFIED}},
// VKEY_A, Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED}},
// VKEY_A, Alt+Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED}},
// VKEY_LWIN (left Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::META},
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::META}},
// VKEY_RWIN (right Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_RWIN, ui::DomCode::META_RIGHT,
ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::META},
{ui::VKEY_RWIN, ui::DomCode::META_RIGHT,
ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::META}},
});
// Simulate the default initialization of the Apple Command key remap pref to
// Ctrl.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
TestExternalAppleKeyboard({
// VKEY_A, Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN,
ui::DomKey::UNIDENTIFIED}},
// VKEY_A, Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'a'>::Character}},
// VKEY_A, Alt+Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'a'>::Character}},
// VKEY_LWIN (left Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::CONTROL}},
// VKEY_RWIN (right Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_RWIN, ui::DomCode::META_RIGHT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_RIGHT,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::CONTROL}},
});
// Now simulate the user remapped the Command key back to Search.
IntegerPrefMember command;
InitModifierKeyPref(&command, prefs::kLanguageRemapExternalCommandKeyTo,
ui::chromeos::ModifierKey::kSearchKey);
TestExternalAppleKeyboard({
// VKEY_A, Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED}},
// VKEY_A, Alt+Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED}},
// VKEY_LWIN (left Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META}},
// VKEY_RWIN (right Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_RWIN, ui::DomCode::META_RIGHT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_LWIN, ui::DomCode::META_RIGHT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META}},
});
}
TEST_F(EventRewriterTest, TestRewriteExternalMetaKey) {
// Simulate the default initialization of the Meta key on external keyboards
// remap pref to Search.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
// By default, the Meta key on all keyboards, internal, external Chrome OS
// branded keyboards, and Generic keyboards should produce Search.
TestNonAppleKeyboardVariants({
// VKEY_A, Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED}},
// VKEY_A, Alt+Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED}},
// VKEY_LWIN (left Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META}},
// VKEY_RWIN (right Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_RWIN, ui::DomCode::META_RIGHT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_LWIN, ui::DomCode::META_RIGHT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META}},
});
// Both preferences for Search on Chrome keyboards, and external Meta on
// generic external keyboards are independent, even if one or both are
// modified.
// Remap Chrome OS Search to Ctrl.
IntegerPrefMember internal_search;
InitModifierKeyPref(&internal_search, prefs::kLanguageRemapSearchKeyTo,
ui::chromeos::ModifierKey::kControlKey);
// Remap external Meta to Alt.
IntegerPrefMember meta;
InitModifierKeyPref(&meta, prefs::kLanguageRemapExternalMetaKeyTo,
ui::chromeos::ModifierKey::kAltKey);
TestChromeKeyboardVariants({
// VKEY_A, Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'a'>::Character}},
// VKEY_A, Alt+Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'a'>::Character}},
// VKEY_LWIN (left Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::CONTROL}},
// VKEY_RWIN (right Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_RWIN, ui::DomCode::META_RIGHT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_RIGHT,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::CONTROL}},
});
TestExternalGenericKeyboard({
// VKEY_A, Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'a'>::Character}},
// VKEY_A, Alt+Win modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'a'>::Character}},
// VKEY_LWIN (left Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN,
ui::DomKey::ALT}},
// VKEY_RWIN (right Windows key), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_RWIN, ui::DomCode::META_RIGHT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_MENU, ui::DomCode::ALT_RIGHT, ui::EF_ALT_DOWN,
ui::DomKey::ALT}},
});
}
// For crbug.com/133896.
TEST_F(EventRewriterTest, TestRewriteCommandToControlWithControlRemapped) {
// Remap Control to Alt.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember control;
InitModifierKeyPref(&control, prefs::kLanguageRemapControlKeyTo,
ui::chromeos::ModifierKey::kAltKey);
TestNonAppleKeyboardVariants({
// Control should be remapped to Alt.
{ui::ET_KEY_PRESSED,
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN,
ui::DomKey::ALT}},
});
// Now verify that remapping does not affect Apple keyboard.
TestExternalAppleKeyboard({
// VKEY_LWIN (left Command key) with Alt modifier. The remapped Command
// key should never be re-remapped to Alt.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::CONTROL}},
// VKEY_RWIN (right Command key) with Alt modifier. The remapped Command
// key should never be re-remapped to Alt.
{ui::ET_KEY_PRESSED,
{ui::VKEY_RWIN, ui::DomCode::META_RIGHT,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::META},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_RIGHT,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::CONTROL}},
});
}
void EventRewriterTest::TestRewriteNumPadKeys() {
// Even if most Chrome OS keyboards do not have numpad, they should still
// handle it the same way as generic PC keyboards.
TestNonAppleKeyboardVariants({
// XK_KP_Insert (= NumPad 0 without Num Lock), no modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_INSERT, ui::DomCode::NUMPAD0, ui::EF_NONE, ui::DomKey::INSERT},
{ui::VKEY_NUMPAD0, ui::DomCode::NUMPAD0, ui::EF_NONE,
ui::DomKey::Constant<'0'>::Character}},
// XK_KP_Insert (= NumPad 0 without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_INSERT, ui::DomCode::NUMPAD0, ui::EF_ALT_DOWN,
ui::DomKey::INSERT},
{ui::VKEY_NUMPAD0, ui::DomCode::NUMPAD0, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'0'>::Character}},
// XK_KP_Delete (= NumPad . without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_DELETE, ui::DomCode::NUMPAD_DECIMAL, ui::EF_ALT_DOWN,
ui::DomKey::DEL},
{ui::VKEY_DECIMAL, ui::DomCode::NUMPAD_DECIMAL, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'.'>::Character}},
// XK_KP_End (= NumPad 1 without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_END, ui::DomCode::NUMPAD1, ui::EF_ALT_DOWN, ui::DomKey::END},
{ui::VKEY_NUMPAD1, ui::DomCode::NUMPAD1, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'1'>::Character}},
// XK_KP_Down (= NumPad 2 without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::NUMPAD2, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_DOWN},
{ui::VKEY_NUMPAD2, ui::DomCode::NUMPAD2, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'2'>::Character}},
// XK_KP_Next (= NumPad 3 without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NEXT, ui::DomCode::NUMPAD3, ui::EF_ALT_DOWN,
ui::DomKey::PAGE_DOWN},
{ui::VKEY_NUMPAD3, ui::DomCode::NUMPAD3, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'3'>::Character}},
// XK_KP_Left (= NumPad 4 without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LEFT, ui::DomCode::NUMPAD4, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_LEFT},
{ui::VKEY_NUMPAD4, ui::DomCode::NUMPAD4, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'4'>::Character}},
// XK_KP_Begin (= NumPad 5 without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_CLEAR, ui::DomCode::NUMPAD5, ui::EF_ALT_DOWN,
ui::DomKey::CLEAR},
{ui::VKEY_NUMPAD5, ui::DomCode::NUMPAD5, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'5'>::Character}},
// XK_KP_Right (= NumPad 6 without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_RIGHT, ui::DomCode::NUMPAD6, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_RIGHT},
{ui::VKEY_NUMPAD6, ui::DomCode::NUMPAD6, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'6'>::Character}},
// XK_KP_Home (= NumPad 7 without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_HOME, ui::DomCode::NUMPAD7, ui::EF_ALT_DOWN, ui::DomKey::HOME},
{ui::VKEY_NUMPAD7, ui::DomCode::NUMPAD7, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'7'>::Character}},
// XK_KP_Up (= NumPad 8 without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::NUMPAD8, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_UP},
{ui::VKEY_NUMPAD8, ui::DomCode::NUMPAD8, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'8'>::Character}},
// XK_KP_Prior (= NumPad 9 without Num Lock), Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_PRIOR, ui::DomCode::NUMPAD9, ui::EF_ALT_DOWN,
ui::DomKey::PAGE_UP},
{ui::VKEY_NUMPAD9, ui::DomCode::NUMPAD9, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'9'>::Character}},
// XK_KP_0 (= NumPad 0 with Num Lock), Num Lock modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD0, ui::DomCode::NUMPAD0, ui::EF_NONE,
ui::DomKey::Constant<'0'>::Character},
{ui::VKEY_NUMPAD0, ui::DomCode::NUMPAD0, ui::EF_NONE,
ui::DomKey::Constant<'0'>::Character}},
// XK_KP_DECIMAL (= NumPad . with Num Lock), Num Lock modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_DECIMAL, ui::DomCode::NUMPAD_DECIMAL, ui::EF_NONE,
ui::DomKey::Constant<'.'>::Character},
{ui::VKEY_DECIMAL, ui::DomCode::NUMPAD_DECIMAL, ui::EF_NONE,
ui::DomKey::Constant<'.'>::Character}},
// XK_KP_1 (= NumPad 1 with Num Lock), Num Lock modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD1, ui::DomCode::NUMPAD1, ui::EF_NONE,
ui::DomKey::Constant<'1'>::Character},
{ui::VKEY_NUMPAD1, ui::DomCode::NUMPAD1, ui::EF_NONE,
ui::DomKey::Constant<'1'>::Character}},
// XK_KP_2 (= NumPad 2 with Num Lock), Num Lock modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD2, ui::DomCode::NUMPAD2, ui::EF_NONE,
ui::DomKey::Constant<'2'>::Character},
{ui::VKEY_NUMPAD2, ui::DomCode::NUMPAD2, ui::EF_NONE,
ui::DomKey::Constant<'2'>::Character}},
// XK_KP_3 (= NumPad 3 with Num Lock), Num Lock modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD3, ui::DomCode::NUMPAD3, ui::EF_NONE,
ui::DomKey::Constant<'3'>::Character},
{ui::VKEY_NUMPAD3, ui::DomCode::NUMPAD3, ui::EF_NONE,
ui::DomKey::Constant<'3'>::Character}},
// XK_KP_4 (= NumPad 4 with Num Lock), Num Lock modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD4, ui::DomCode::NUMPAD4, ui::EF_NONE,
ui::DomKey::Constant<'4'>::Character},
{ui::VKEY_NUMPAD4, ui::DomCode::NUMPAD4, ui::EF_NONE,
ui::DomKey::Constant<'4'>::Character}},
// XK_KP_5 (= NumPad 5 with Num Lock), Num Lock
// modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD5, ui::DomCode::NUMPAD5, ui::EF_NONE,
ui::DomKey::Constant<'5'>::Character},
{ui::VKEY_NUMPAD5, ui::DomCode::NUMPAD5, ui::EF_NONE,
ui::DomKey::Constant<'5'>::Character}},
// XK_KP_6 (= NumPad 6 with Num Lock), Num Lock
// modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD6, ui::DomCode::NUMPAD6, ui::EF_NONE,
ui::DomKey::Constant<'6'>::Character},
{ui::VKEY_NUMPAD6, ui::DomCode::NUMPAD6, ui::EF_NONE,
ui::DomKey::Constant<'6'>::Character}},
// XK_KP_7 (= NumPad 7 with Num Lock), Num Lock
// modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD7, ui::DomCode::NUMPAD7, ui::EF_NONE,
ui::DomKey::Constant<'7'>::Character},
{ui::VKEY_NUMPAD7, ui::DomCode::NUMPAD7, ui::EF_NONE,
ui::DomKey::Constant<'7'>::Character}},
// XK_KP_8 (= NumPad 8 with Num Lock), Num Lock
// modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD8, ui::DomCode::NUMPAD8, ui::EF_NONE,
ui::DomKey::Constant<'8'>::Character},
{ui::VKEY_NUMPAD8, ui::DomCode::NUMPAD8, ui::EF_NONE,
ui::DomKey::Constant<'8'>::Character}},
// XK_KP_9 (= NumPad 9 with Num Lock), Num Lock
// modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD9, ui::DomCode::NUMPAD9, ui::EF_NONE,
ui::DomKey::Constant<'9'>::Character},
{ui::VKEY_NUMPAD9, ui::DomCode::NUMPAD9, ui::EF_NONE,
ui::DomKey::Constant<'9'>::Character}},
});
}
TEST_F(EventRewriterTest, TestRewriteNumPadKeys) {
TestRewriteNumPadKeys();
}
// Tests if the rewriter can handle a Command + Num Pad event.
void EventRewriterTest::TestRewriteNumPadKeysOnAppleKeyboard() {
// Simulate the default initialization of the Apple Command key remap pref to
// Ctrl.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
TestExternalAppleKeyboard({
// XK_KP_End (= NumPad 1 without Num Lock), Win modifier.
// The result should be "Num Pad 1 with Control + Num Lock modifiers".
{ui::ET_KEY_PRESSED,
{ui::VKEY_END, ui::DomCode::NUMPAD1, ui::EF_COMMAND_DOWN,
ui::DomKey::END},
{ui::VKEY_NUMPAD1, ui::DomCode::NUMPAD1, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'1'>::Character}},
// XK_KP_1 (= NumPad 1 with Num Lock), Win modifier.
// The result should also be "Num Pad 1 with Control + Num Lock
// modifiers".
{ui::ET_KEY_PRESSED,
{ui::VKEY_NUMPAD1, ui::DomCode::NUMPAD1, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'1'>::Character},
{ui::VKEY_NUMPAD1, ui::DomCode::NUMPAD1, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'1'>::Character}},
});
}
TEST_F(EventRewriterTest, TestRewriteNumPadKeysOnAppleKeyboard) {
TestRewriteNumPadKeysOnAppleKeyboard();
}
TEST_F(EventRewriterTest, TestRewriteModifiersNoRemap) {
TestAllKeyboardVariants({
// Press Search. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_NONE, ui::DomKey::META},
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_COMMAND_DOWN,
ui::DomKey::META}},
// Press left Control. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL}},
// Press right Control. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL}},
// Press left Alt. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN, ui::DomKey::ALT},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN,
ui::DomKey::ALT}},
// Press right Alt. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN, ui::DomKey::ALT},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN,
ui::DomKey::ALT}},
// Test KeyRelease event, just in case.
// Release Search. Confirm the release event is not rewritten.
{ui::ET_KEY_RELEASED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_NONE, ui::DomKey::META},
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_NONE, ui::DomKey::META}},
});
}
TEST_F(EventRewriterTest, TestRewriteModifiersNoRemapMultipleKeys) {
TestAllKeyboardVariants({
// Press Alt with Shift. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT,
ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ALT},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT,
ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ALT}},
// Press Escape with Alt and Shift. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE,
ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ESCAPE},
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE,
ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ESCAPE}},
// Press Search with Caps Lock mask. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_CAPS_LOCK_ON | ui::EF_COMMAND_DOWN, ui::DomKey::META},
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_CAPS_LOCK_ON | ui::EF_COMMAND_DOWN, ui::DomKey::META}},
// Release Search with Caps Lock mask. Confirm the event is not rewritten.
{ui::ET_KEY_RELEASED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_CAPS_LOCK_ON,
ui::DomKey::META},
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_CAPS_LOCK_ON,
ui::DomKey::META}},
// Press Shift+Ctrl+Alt+Search+Escape. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
ui::EF_COMMAND_DOWN,
ui::DomKey::ESCAPE},
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
ui::EF_COMMAND_DOWN,
ui::DomKey::ESCAPE}},
// Press Shift+Ctrl+Alt+Search+B. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_B, ui::DomCode::US_B,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'B'>::Character},
{ui::VKEY_B, ui::DomCode::US_B,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'B'>::Character}},
});
}
TEST_F(EventRewriterTest, TestRewriteModifiersDisableSome) {
// Disable Search, Control and Escape keys.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember search;
InitModifierKeyPref(&search, prefs::kLanguageRemapSearchKeyTo,
ui::chromeos::ModifierKey::kVoidKey);
IntegerPrefMember control;
InitModifierKeyPref(&control, prefs::kLanguageRemapControlKeyTo,
ui::chromeos::ModifierKey::kVoidKey);
IntegerPrefMember escape;
InitModifierKeyPref(&escape, prefs::kLanguageRemapEscapeKeyTo,
ui::chromeos::ModifierKey::kVoidKey);
TestChromeKeyboardVariants({
// Press Alt with Shift. This key press shouldn't be affected by the
// pref. Confirm the event is not rewritten.
{ui::ET_KEY_PRESSED,
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT,
ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ALT},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT,
ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ALT}},
// Press Search. Confirm the event is now VKEY_UNKNOWN.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_NONE, ui::DomKey::META},
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_NONE,
ui::DomKey::UNIDENTIFIED}},
// Press Control. Confirm the event is now VKEY_UNKNOWN.
{ui::ET_KEY_PRESSED,
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL},
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_NONE,
ui::DomKey::UNIDENTIFIED}},
// Press Escape. Confirm the event is now VKEY_UNKNOWN.
{ui::ET_KEY_PRESSED,
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE, ui::EF_NONE, ui::DomKey::ESCAPE},
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_NONE,
ui::DomKey::UNIDENTIFIED}},
// Press Control+Search. Confirm the event is now VKEY_UNKNOWN
// without any modifiers.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::META},
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_NONE,
ui::DomKey::UNIDENTIFIED}},
// Press Control+Search+a. Confirm the event is now VKEY_A without any
// modifiers.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'a'>::Character},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_NONE,
ui::DomKey::Constant<'a'>::Character}},
// Press Control+Search+Alt+a. Confirm the event is now VKEY_A only with
// the Alt modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
ui::DomKey::Constant<'a'>::Character},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'a'>::Character}},
});
// Remap Alt to Control.
IntegerPrefMember alt;
InitModifierKeyPref(&alt, prefs::kLanguageRemapAltKeyTo,
ui::chromeos::ModifierKey::kControlKey);
TestChromeKeyboardVariants({
// Press left Alt. Confirm the event is now VKEY_CONTROL
// even though the Control key itself is disabled.
{ui::ET_KEY_PRESSED,
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN, ui::DomKey::ALT},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL}},
// Press Alt+a. Confirm the event is now Control+a even though the Control
// key itself is disabled.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'a'>::Character},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'a'>::Character}},
});
}
TEST_F(EventRewriterTest, TestRewriteModifiersRemapToControl) {
// Remap Search to Control.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember search;
InitModifierKeyPref(&search, prefs::kLanguageRemapSearchKeyTo,
ui::chromeos::ModifierKey::kControlKey);
TestChromeKeyboardVariants({
// Press Search. Confirm the event is now VKEY_CONTROL.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_COMMAND_DOWN,
ui::DomKey::META},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL}},
});
// Remap Alt to Control too.
IntegerPrefMember alt;
InitModifierKeyPref(&alt, prefs::kLanguageRemapAltKeyTo,
ui::chromeos::ModifierKey::kControlKey);
TestChromeKeyboardVariants({
// Press Alt. Confirm the event is now VKEY_CONTROL.
{ui::ET_KEY_PRESSED,
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN, ui::DomKey::ALT},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL}},
// Press Alt+Search. Confirm the event is now VKEY_CONTROL.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::META},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL}},
// Press Control+Alt+Search. Confirm the event is now VKEY_CONTROL.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::META},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL}},
// Press Shift+Control+Alt+Search. Confirm the event is now Control with
// Shift and Control modifiers.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
ui::EF_COMMAND_DOWN,
ui::DomKey::META},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::CONTROL}},
// Press Shift+Control+Alt+Search+B. Confirm the event is now B with Shift
// and Control modifiers.
{ui::ET_KEY_PRESSED,
{ui::VKEY_B, ui::DomCode::US_B,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'B'>::Character},
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'B'>::Character}},
});
}
TEST_F(EventRewriterTest, TestRewriteModifiersRemapToEscape) {
// Remap Search to Escape.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember search;
InitModifierKeyPref(&search, prefs::kLanguageRemapSearchKeyTo,
ui::chromeos::ModifierKey::kEscapeKey);
TestChromeKeyboardVariants({
// Press Search. Confirm the event is now VKEY_ESCAPE.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_COMMAND_DOWN,
ui::DomKey::META},
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE, ui::EF_NONE, ui::DomKey::ESCAPE}},
});
}
TEST_F(EventRewriterTest, TestRewriteModifiersRemapEscapeToAlt) {
// Remap Escape to Alt.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember escape;
InitModifierKeyPref(&escape, prefs::kLanguageRemapEscapeKeyTo,
ui::chromeos::ModifierKey::kAltKey);
TestAllKeyboardVariants({
// Press Escape. Confirm the event is now VKEY_MENU.
{ui::ET_KEY_PRESSED,
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE, ui::EF_NONE, ui::DomKey::ESCAPE},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN,
ui::DomKey::ALT}},
// Release Escape to clear flags.
{ui::ET_KEY_RELEASED,
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE, ui::EF_NONE, ui::DomKey::ESCAPE},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_NONE, ui::DomKey::ALT}},
});
}
TEST_F(EventRewriterTest, TestRewriteModifiersRemapAltToControl) {
// Remap Alt to Control.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember alt;
InitModifierKeyPref(&alt, prefs::kLanguageRemapAltKeyTo,
ui::chromeos::ModifierKey::kControlKey);
TestAllKeyboardVariants({
// Press left Alt. Confirm the event is now VKEY_CONTROL.
{ui::ET_KEY_PRESSED,
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN, ui::DomKey::ALT},
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL}},
// Press Shift+comma. Verify that only the flags are changed.
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_COMMA, ui::DomCode::COMMA,
ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_OEM_COMMA, ui::DomCode::COMMA,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'<'>::Character}},
// Press Shift+9. Verify that only the flags are changed.
{ui::ET_KEY_PRESSED,
{ui::VKEY_9, ui::DomCode::DIGIT9, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_9, ui::DomCode::DIGIT9,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'('>::Character}},
});
}
TEST_F(EventRewriterTest, TestRewriteModifiersRemapUnderEscapeControlAlt) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
// Remap Escape to Alt.
IntegerPrefMember escape;
InitModifierKeyPref(&escape, prefs::kLanguageRemapEscapeKeyTo,
ui::chromeos::ModifierKey::kAltKey);
// Remap Alt to Control.
IntegerPrefMember alt;
InitModifierKeyPref(&alt, prefs::kLanguageRemapAltKeyTo,
ui::chromeos::ModifierKey::kControlKey);
// Remap Control to Search.
IntegerPrefMember control;
InitModifierKeyPref(&control, prefs::kLanguageRemapControlKeyTo,
ui::chromeos::ModifierKey::kSearchKey);
TestAllKeyboardVariants({
// Press left Control. Confirm the event is now VKEY_LWIN.
{ui::ET_KEY_PRESSED,
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN,
ui::DomKey::CONTROL},
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_COMMAND_DOWN,
ui::DomKey::META}},
// Then, press all of the three, Control+Alt+Escape.
{ui::ET_KEY_PRESSED,
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::CONTROL},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
ui::DomKey::ALT}},
// Press Shift+Control+Alt+Escape.
{ui::ET_KEY_PRESSED,
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
ui::DomKey::ESCAPE},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
ui::EF_COMMAND_DOWN,
ui::DomKey::ALT}},
// Press Shift+Control+Alt+B
{ui::ET_KEY_PRESSED,
{ui::VKEY_B, ui::DomCode::US_B,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
ui::DomKey::Constant<'B'>::Character},
{ui::VKEY_B, ui::DomCode::US_B,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'B'>::Character}},
});
}
TEST_F(EventRewriterTest,
TestRewriteModifiersRemapUnderEscapeControlAltSearch) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
// Remap Escape to Alt.
IntegerPrefMember escape;
InitModifierKeyPref(&escape, prefs::kLanguageRemapEscapeKeyTo,
ui::chromeos::ModifierKey::kAltKey);
// Remap Alt to Control.
IntegerPrefMember alt;
InitModifierKeyPref(&alt, prefs::kLanguageRemapAltKeyTo,
ui::chromeos::ModifierKey::kControlKey);
// Remap Control to Search.
IntegerPrefMember control;
InitModifierKeyPref(&control, prefs::kLanguageRemapControlKeyTo,
ui::chromeos::ModifierKey::kSearchKey);
// Remap Search to Backspace.
IntegerPrefMember search;
InitModifierKeyPref(&search, prefs::kLanguageRemapSearchKeyTo,
ui::chromeos::ModifierKey::kBackspaceKey);
TestChromeKeyboardVariants({
// Release Control and Escape, as Search and Alt would transform Backspace
// to Delete.
{ui::ET_KEY_PRESSED,
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_NONE,
ui::DomKey::CONTROL},
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_COMMAND_DOWN,
ui::DomKey::META}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE, ui::EF_NONE, ui::DomKey::ESCAPE},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN,
ui::DomKey::ALT}},
{ui::ET_KEY_RELEASED,
{ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_NONE,
ui::DomKey::CONTROL},
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_ALT_DOWN,
ui::DomKey::META}},
{ui::ET_KEY_RELEASED,
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE, ui::EF_NONE, ui::DomKey::ESCAPE},
{ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_NONE, ui::DomKey::ALT}},
// Press Search. Confirm the event is now VKEY_BACK.
{ui::ET_KEY_PRESSED,
{ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_COMMAND_DOWN,
ui::DomKey::META},
{ui::VKEY_BACK, ui::DomCode::BACKSPACE, ui::EF_NONE,
ui::DomKey::BACKSPACE}},
});
}
TEST_F(EventRewriterTest, TestRewriteModifiersRemapBackspaceToEscape) {
// Remap Backspace to Escape.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember backspace;
InitModifierKeyPref(&backspace, prefs::kLanguageRemapBackspaceKeyTo,
ui::chromeos::ModifierKey::kEscapeKey);
TestAllKeyboardVariants({
// Press Backspace. Confirm the event is now VKEY_ESCAPE.
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE, ui::EF_NONE,
ui::DomKey::BACKSPACE},
{ui::VKEY_ESCAPE, ui::DomCode::ESCAPE, ui::EF_NONE, ui::DomKey::ESCAPE}},
});
}
TEST_F(EventRewriterTest, TestRewriteModifiersRemapToCapsLock) {
// Remap Search to Caps Lock.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember search;
InitModifierKeyPref(&search, prefs::kLanguageRemapSearchKeyTo,
ui::chromeos::ModifierKey::kCapsLockKey);
SetupKeyboard("Internal Keyboard");
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Press Search.
EXPECT_EQ(
GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK,
ui::EF_MOD3_DOWN | ui::EF_CAPS_LOCK_ON,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_PRESSED, ui::VKEY_LWIN,
ui::DomCode::META_LEFT, ui::EF_COMMAND_DOWN,
ui::DomKey::META, kNoScanCode));
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Release Search.
EXPECT_EQ(
GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK, ui::EF_NONE,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_RELEASED, ui::VKEY_LWIN,
ui::DomCode::META_LEFT, ui::EF_NONE,
ui::DomKey::META, kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Press Search.
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON | ui::EF_MOD3_DOWN,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_PRESSED,
ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_CAPS_LOCK_ON,
ui::DomKey::META, kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Release Search.
EXPECT_EQ(
GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK, ui::EF_NONE,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_RELEASED, ui::VKEY_LWIN,
ui::DomCode::META_LEFT, ui::EF_NONE,
ui::DomKey::META, kNoScanCode));
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Do the same on external Chrome OS keyboard.
SetupKeyboard("External Chrome Keyboard", kKbdTopRowLayout1Tag,
ui::INPUT_DEVICE_UNKNOWN);
// Press Search.
EXPECT_EQ(
GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK,
ui::EF_MOD3_DOWN | ui::EF_CAPS_LOCK_ON,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_PRESSED, ui::VKEY_LWIN,
ui::DomCode::META_LEFT, ui::EF_COMMAND_DOWN,
ui::DomKey::META, kNoScanCode));
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Release Search.
EXPECT_EQ(
GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK, ui::EF_NONE,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_RELEASED, ui::VKEY_LWIN,
ui::DomCode::META_LEFT, ui::EF_NONE,
ui::DomKey::META, kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Press Search.
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON | ui::EF_MOD3_DOWN,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_PRESSED,
ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_CAPS_LOCK_ON,
ui::DomKey::META, kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Release Search.
EXPECT_EQ(
GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK, ui::EF_NONE,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_RELEASED, ui::VKEY_LWIN,
ui::DomCode::META_LEFT, ui::EF_NONE,
ui::DomKey::META, kNoScanCode));
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Try external keyboard with Caps Lock.
SetupKeyboard("External Generic Keyboard", kKbdTopRowLayoutUnspecified,
ui::INPUT_DEVICE_UNKNOWN);
// Press Caps Lock.
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON | ui::EF_MOD3_DOWN,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_PRESSED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON | ui::EF_MOD3_DOWN,
ui::DomKey::CAPS_LOCK, kNoScanCode));
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Release Caps Lock.
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK, ui::EF_NONE,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_RELEASED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_NONE, ui::DomKey::CAPS_LOCK,
kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
}
TEST_F(EventRewriterTest, TestRewriteCapsLock) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
SetupKeyboard("External Generic Keyboard", kKbdTopRowLayoutUnspecified,
ui::INPUT_DEVICE_UNKNOWN);
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
// On Chrome OS, CapsLock is mapped to CapsLock with Mod3Mask.
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON | ui::EF_MOD3_DOWN,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_PRESSED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK,
kNoScanCode));
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK, ui::EF_NONE,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_RELEASED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK,
kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Remap Caps Lock to Control.
IntegerPrefMember caps_lock;
InitModifierKeyPref(&caps_lock, prefs::kLanguageRemapCapsLockKeyTo,
ui::chromeos::ModifierKey::kControlKey);
// Press Caps Lock. CapsLock is enabled but we have remapped the key to
// now be Control. We want to ensure that the CapsLock modifier is still
// active even after pressing the remapped Capslock key.
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL,
ui::DomCode::CONTROL_LEFT,
ui::EF_CONTROL_DOWN | ui::EF_CAPS_LOCK_ON,
ui::DomKey::CONTROL, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_PRESSED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON | ui::EF_MOD3_DOWN,
ui::DomKey::CAPS_LOCK, kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Release Caps Lock.
EXPECT_EQ(
GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL,
ui::DomCode::CONTROL_LEFT, ui::EF_CAPS_LOCK_ON,
ui::DomKey::CONTROL, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_RELEASED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON, ui::DomKey::CAPS_LOCK,
kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
}
TEST_F(EventRewriterTest, TestRewriteExternalCapsLockWithDifferentScenarios) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
SetupKeyboard("External Generic Keyboard", kKbdTopRowLayoutUnspecified,
ui::INPUT_DEVICE_UNKNOWN);
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Turn on CapsLock.
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON | ui::EF_MOD3_DOWN,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_PRESSED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK,
kNoScanCode));
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK, ui::EF_NONE,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_RELEASED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK,
kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Remap CapsLock to Search.
IntegerPrefMember search;
InitModifierKeyPref(&search, prefs::kLanguageRemapCapsLockKeyTo,
ui::chromeos::ModifierKey::kSearchKey);
// Now that CapsLock is enabled, press the remapped CapsLock button again
// and expect to not disable CapsLock.
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_LWIN,
ui::DomCode::META_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_CAPS_LOCK_ON,
ui::DomKey::META, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_PRESSED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON,
ui::DomKey::CAPS_LOCK, kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
EXPECT_EQ(GetExpectedResultAsString(
ui::ET_KEY_RELEASED, ui::VKEY_LWIN, ui::DomCode::META_LEFT,
ui::EF_CAPS_LOCK_ON, ui::DomKey::META, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_RELEASED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON,
ui::DomKey::CAPS_LOCK, kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Remap CapsLock key back to CapsLock.
IntegerPrefMember capslock;
InitModifierKeyPref(&capslock, prefs::kLanguageRemapCapsLockKeyTo,
ui::chromeos::ModifierKey::kCapsLockKey);
// Now press CapsLock again and now expect that the CapsLock modifier is
// removed.
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK,
ui::EF_CAPS_LOCK_ON | ui::EF_MOD3_DOWN,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_PRESSED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK,
kNoScanCode));
EXPECT_TRUE(fake_ime_keyboard_.caps_lock_is_enabled_);
// Disabling CapsLocks only happens on release of the CapsLock key.
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL,
ui::DomCode::CAPS_LOCK, ui::EF_NONE,
ui::DomKey::CAPS_LOCK, kNoScanCode),
GetRewrittenEventAsString(rewriter(), ui::ET_KEY_RELEASED,
ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
ui::EF_NONE, ui::DomKey::CAPS_LOCK,
kNoScanCode));
EXPECT_FALSE(fake_ime_keyboard_.caps_lock_is_enabled_);
}
TEST_F(EventRewriterTest, TestRewriteCapsLockToControl) {
// Remap CapsLock to Control.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember control;
InitModifierKeyPref(&control, prefs::kLanguageRemapCapsLockKeyTo,
ui::chromeos::ModifierKey::kControlKey);
TestExternalGenericKeyboard({
// Press CapsLock+a. Confirm that Mod3Mask is rewritten to ControlMask.
// On Chrome OS, CapsLock works as a Mod3 modifier.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_MOD3_DOWN,
ui::DomKey::Constant<'a'>::Character},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'a'>::Character}},
// Press Control+CapsLock+a. Confirm that Mod3Mask is rewritten to
// ControlMask
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_CONTROL_DOWN | ui::EF_MOD3_DOWN,
ui::DomKey::Constant<'a'>::Character},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'a'>::Character}},
// Press Alt+CapsLock+a. Confirm that Mod3Mask is rewritten to
// ControlMask.
{ui::ET_KEY_PRESSED,
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_MOD3_DOWN,
ui::DomKey::Constant<'a'>::Character},
{ui::VKEY_A, ui::DomCode::US_A, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'a'>::Character}},
});
}
TEST_F(EventRewriterTest, TestRewriteCapsLockMod3InUse) {
// Remap CapsLock to Control.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember control;
InitModifierKeyPref(&control, prefs::kLanguageRemapCapsLockKeyTo,
ui::chromeos::ModifierKey::kControlKey);
SetupKeyboard("External Generic Keyboard", kKbdTopRowLayoutUnspecified,
ui::INPUT_DEVICE_UNKNOWN);
input_method_manager_mock_->set_mod3_used(true);
// Press CapsLock+a. Confirm that Mod3Mask is NOT rewritten to ControlMask
// when Mod3Mask is already in use by the current XKB layout.
EXPECT_EQ(
GetExpectedResultAsString(
ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::US_A, ui::EF_NONE,
ui::DomKey::Constant<'a'>::Character, kNoScanCode),
GetRewrittenEventAsString(
rewriter(), ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::US_A,
ui::EF_NONE, ui::DomKey::Constant<'a'>::Character, kNoScanCode));
input_method_manager_mock_->set_mod3_used(false);
}
// TODO(crbug.com/1179893): Remove once the feature is enabled permanently.
TEST_F(EventRewriterTest, TestRewriteExtendedKeysAltVariantsOld) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
scoped_feature_list_.InitAndDisableFeature(
::features::kImprovedKeyboardShortcuts);
TestNonAppleKeyboardVariants({
// Alt+Backspace -> Delete
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE, ui::EF_ALT_DOWN,
ui::DomKey::BACKSPACE},
{ui::VKEY_DELETE, ui::DomCode::DEL, ui::EF_NONE, ui::DomKey::DEL}},
// Control+Alt+Backspace -> Control+Delete
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::BACKSPACE},
{ui::VKEY_DELETE, ui::DomCode::DEL, ui::EF_CONTROL_DOWN,
ui::DomKey::DEL}},
// Search+Alt+Backspace -> Alt+Backspace
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::BACKSPACE},
{ui::VKEY_BACK, ui::DomCode::BACKSPACE, ui::EF_ALT_DOWN,
ui::DomKey::BACKSPACE}},
// Search+Control+Alt+Backspace -> Control+Alt+Backspace
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::BACKSPACE},
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::BACKSPACE}},
// Alt+Up -> Prior
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::ARROW_UP, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_UP},
{ui::VKEY_PRIOR, ui::DomCode::PAGE_UP, ui::EF_NONE,
ui::DomKey::PAGE_UP}},
// Alt+Down -> Next
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_DOWN},
{ui::VKEY_NEXT, ui::DomCode::PAGE_DOWN, ui::EF_NONE,
ui::DomKey::PAGE_DOWN}},
// Ctrl+Alt+Up -> Home
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_UP},
{ui::VKEY_HOME, ui::DomCode::HOME, ui::EF_NONE, ui::DomKey::HOME}},
// Ctrl+Alt+Down -> End
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_DOWN},
{ui::VKEY_END, ui::DomCode::END, ui::EF_NONE, ui::DomKey::END}},
// NOTE: The following are workarounds to avoid rewriting the
// Alt variants by additionally pressing Search.
// Search+Ctrl+Alt+Up -> Ctrl+Alt+Up
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::ARROW_UP},
{ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_UP}},
// Search+Ctrl+Alt+Down -> Ctrl+Alt+Down
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::ARROW_DOWN},
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_DOWN}},
});
}
// TODO(crbug.com/1179893): Remove once the feature is enabled permanently.
// For M92 kImprovedKeyboardShortcuts is enabled but kDeprecateAltBasedSixPack
// is disabled.
TEST_F(EventRewriterTest, TestRewriteExtendedKeysAltVariantsM92) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
TestNonAppleKeyboardVariants({
// Alt+Backspace -> Delete
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE, ui::EF_ALT_DOWN,
ui::DomKey::BACKSPACE},
{ui::VKEY_DELETE, ui::DomCode::DEL, ui::EF_NONE, ui::DomKey::DEL}},
// Control+Alt+Backspace -> Control+Delete
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::BACKSPACE},
{ui::VKEY_DELETE, ui::DomCode::DEL, ui::EF_CONTROL_DOWN,
ui::DomKey::DEL}},
// Search+Alt+Backspace -> Alt+Backspace
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::BACKSPACE},
{ui::VKEY_BACK, ui::DomCode::BACKSPACE, ui::EF_ALT_DOWN,
ui::DomKey::BACKSPACE}},
// Search+Control+Alt+Backspace -> Control+Alt+Backspace
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::BACKSPACE},
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::BACKSPACE}},
// Alt+Up -> Prior
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::ARROW_UP, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_UP},
{ui::VKEY_PRIOR, ui::DomCode::PAGE_UP, ui::EF_NONE,
ui::DomKey::PAGE_UP}},
// Alt+Down -> Next
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_DOWN},
{ui::VKEY_NEXT, ui::DomCode::PAGE_DOWN, ui::EF_NONE,
ui::DomKey::PAGE_DOWN}},
// Ctrl+Alt+Up -> Home
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_UP},
{ui::VKEY_HOME, ui::DomCode::HOME, ui::EF_NONE, ui::DomKey::HOME}},
// Ctrl+Alt+Down -> End
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_DOWN},
{ui::VKEY_END, ui::DomCode::END, ui::EF_NONE, ui::DomKey::END}},
// NOTE: The following are workarounds to avoid rewriting the
// Alt variants by additionally pressing Search.
// Search+Ctrl+Alt+Up -> Ctrl+Alt+Up
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::ARROW_UP},
{ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_UP}},
// Search+Ctrl+Alt+Down -> Ctrl+Alt+Down
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::ARROW_DOWN},
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN,
ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_DOWN}},
});
}
// TODO(crbug.com/1179893): Remove once the feature is enabled permanently.
// This is the intended final state with both kImprovedKeyboardShortcuts and
// kDeprecateAltBasedSixPack enabled.
TEST_F(EventRewriterTest, TestRewriteExtendedKeysAltVariants) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
scoped_feature_list_.InitAndEnableFeature(
::features::kDeprecateAltBasedSixPack);
// All the previously supported Alt based rewrites no longer have any
// effect. The Search workarounds no longer take effect and the Search+Key
// portion is rewritten as expected.
TestNonAppleKeyboardVariants({
// Alt+Backspace -> No Rewrite
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE, ui::EF_ALT_DOWN,
ui::DomKey::BACKSPACE},
{ui::VKEY_BACK, ui::DomCode::BACKSPACE, ui::EF_ALT_DOWN,
ui::DomKey::BACKSPACE},
kKeyboardDeviceId,
/*triggers_notification=*/true},
// Control+Alt+Backspace -> No Rewrite
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::BACKSPACE},
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::BACKSPACE},
kKeyboardDeviceId,
/*triggers_notification=*/true},
// Search+Alt+Backspace -> Alt+Delete
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::BACKSPACE},
{ui::VKEY_DELETE, ui::DomCode::DEL, ui::EF_ALT_DOWN, ui::DomKey::DEL}},
// Search+Control+Alt+Backspace -> Control+Alt+Delete
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
ui::DomKey::BACKSPACE},
{ui::VKEY_DELETE, ui::DomCode::DEL,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::DEL}},
// Alt+Up -> No Rewrite
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::ARROW_UP, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_UP},
{ui::VKEY_UP, ui::DomCode::ARROW_UP, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_UP},
kKeyboardDeviceId,
/*triggers_notification=*/true},
// Alt+Down -> No Rewrite
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_DOWN},
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN, ui::EF_ALT_DOWN,
ui::DomKey::ARROW_DOWN},
kKeyboardDeviceId,
/*triggers_notification=*/true},
// Ctrl+Alt+Up -> No Rewrite
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ARROW_UP},
{ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ARROW_UP},
kKeyboardDeviceId,
/*triggers_notification=*/true},
// Ctrl+Alt+Down -> No Rewrite
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ARROW_DOWN},
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ARROW_DOWN},
kKeyboardDeviceId,
/*triggers_notification=*/true},
// NOTE: The following were workarounds to avoid rewriting the
// Alt variants by additionally pressing Search.
// Search+Ctrl+Alt+Up -> Ctrl+Alt+PageUp(aka Prior)
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
ui::DomKey::ARROW_UP},
{ui::VKEY_PRIOR, ui::DomCode::PAGE_UP,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::PAGE_UP}},
// Search+Ctrl+Alt+Down -> Ctrl+Alt+PageDown(aka Next)
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
ui::DomKey::ARROW_DOWN},
{ui::VKEY_NEXT, ui::DomCode::PAGE_DOWN,
ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::PAGE_DOWN}},
});
}
// TODO(crbug.com/1179893): Remove once the feature is enabled permanently.
TEST_F(EventRewriterTest, TestRewriteExtendedKeyInsertOld) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
scoped_feature_list_.InitAndDisableFeature(
::features::kImprovedKeyboardShortcuts);
TestNonAppleKeyboardVariants({
// Period -> Period
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_NONE,
ui::DomKey::Constant<'.'>::Character},
{ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_NONE,
ui::DomKey::Constant<'.'>::Character}},
// Search+Period -> Insert
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'.'>::Character},
{ui::VKEY_INSERT, ui::DomCode::INSERT, ui::EF_NONE, ui::DomKey::INSERT}},
// Control+Search+Period -> Control+Insert
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'.'>::Character},
{ui::VKEY_INSERT, ui::DomCode::INSERT, ui::EF_CONTROL_DOWN,
ui::DomKey::INSERT}},
});
}
TEST_F(EventRewriterTest, TestRewriteExtendedKeyInsertDeprecatedNotification) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
scoped_feature_list_.InitAndEnableFeature(
::features::kImprovedKeyboardShortcuts);
TestNonAppleKeyboardVariants({
// Period -> Period
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_NONE,
ui::DomKey::Constant<'.'>::Character},
{ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_NONE,
ui::DomKey::Constant<'.'>::Character}},
// Search+Period -> No rewrite (and shows notification)
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'.'>::Character},
{ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'.'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
// Control+Search+Period -> No rewrite (and shows notification)
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'.'>::Character},
{ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'.'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
});
}
// TODO(crbug.com/1179893): Rename once the feature is enabled permanently.
TEST_F(EventRewriterTest, TestRewriteExtendedKeyInsertNew) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
scoped_feature_list_.InitAndEnableFeature(
::features::kImprovedKeyboardShortcuts);
TestNonAppleKeyboardVariants({
// Search+Shift+Backspace -> Insert
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN, ui::DomKey::BACKSPACE},
{ui::VKEY_INSERT, ui::DomCode::INSERT, ui::EF_NONE, ui::DomKey::INSERT}},
// Control+Search+Shift+Backspace -> Control+Insert
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE,
ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN,
ui::DomKey::BACKSPACE},
{ui::VKEY_INSERT, ui::DomCode::INSERT, ui::EF_CONTROL_DOWN,
ui::DomKey::INSERT}},
});
}
TEST_F(EventRewriterTest, TestRewriteExtendedKeysSearchVariants) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
TestNonAppleKeyboardVariants({
// Search+Backspace -> Delete
{ui::ET_KEY_PRESSED,
{ui::VKEY_BACK, ui::DomCode::BACKSPACE, ui::EF_COMMAND_DOWN,
ui::DomKey::BACKSPACE},
{ui::VKEY_DELETE, ui::DomCode::DEL, ui::EF_NONE, ui::DomKey::DEL}},
// Search+Up -> Prior
{ui::ET_KEY_PRESSED,
{ui::VKEY_UP, ui::DomCode::ARROW_UP, ui::EF_COMMAND_DOWN,
ui::DomKey::ARROW_UP},
{ui::VKEY_PRIOR, ui::DomCode::PAGE_UP, ui::EF_NONE,
ui::DomKey::PAGE_UP}},
// Search+Down -> Next
{ui::ET_KEY_PRESSED,
{ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN, ui::EF_COMMAND_DOWN,
ui::DomKey::ARROW_DOWN},
{ui::VKEY_NEXT, ui::DomCode::PAGE_DOWN, ui::EF_NONE,
ui::DomKey::PAGE_DOWN}},
// Search+Left -> Home
{ui::ET_KEY_PRESSED,
{ui::VKEY_LEFT, ui::DomCode::ARROW_LEFT, ui::EF_COMMAND_DOWN,
ui::DomKey::ARROW_LEFT},
{ui::VKEY_HOME, ui::DomCode::HOME, ui::EF_NONE, ui::DomKey::HOME}},
// Control+Search+Left -> Control+Home
{ui::ET_KEY_PRESSED,
{ui::VKEY_LEFT, ui::DomCode::ARROW_LEFT,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_LEFT},
{ui::VKEY_HOME, ui::DomCode::HOME, ui::EF_CONTROL_DOWN,
ui::DomKey::HOME}},
// Search+Right -> End
{ui::ET_KEY_PRESSED,
{ui::VKEY_RIGHT, ui::DomCode::ARROW_RIGHT, ui::EF_COMMAND_DOWN,
ui::DomKey::ARROW_RIGHT},
{ui::VKEY_END, ui::DomCode::END, ui::EF_NONE, ui::DomKey::END}},
// Control+Search+Right -> Control+End
{ui::ET_KEY_PRESSED,
{ui::VKEY_RIGHT, ui::DomCode::ARROW_RIGHT,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_RIGHT},
{ui::VKEY_END, ui::DomCode::END, ui::EF_CONTROL_DOWN, ui::DomKey::END}},
});
}
TEST_F(EventRewriterTest, TestNumberRowIsNotRewritten) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
TestNonAppleNonCustomLayoutKeyboardVariants({
// The number row should not be rewritten without Search key.
{ui::ET_KEY_PRESSED,
{ui::VKEY_1, ui::DomCode::DIGIT1, ui::EF_NONE,
ui::DomKey::Constant<'1'>::Character},
{ui::VKEY_1, ui::DomCode::DIGIT1, ui::EF_NONE,
ui::DomKey::Constant<'1'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_2, ui::DomCode::DIGIT2, ui::EF_NONE,
ui::DomKey::Constant<'2'>::Character},
{ui::VKEY_2, ui::DomCode::DIGIT2, ui::EF_NONE,
ui::DomKey::Constant<'2'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_3, ui::DomCode::DIGIT3, ui::EF_NONE,
ui::DomKey::Constant<'3'>::Character},
{ui::VKEY_3, ui::DomCode::DIGIT3, ui::EF_NONE,
ui::DomKey::Constant<'3'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_4, ui::DomCode::DIGIT4, ui::EF_NONE,
ui::DomKey::Constant<'4'>::Character},
{ui::VKEY_4, ui::DomCode::DIGIT4, ui::EF_NONE,
ui::DomKey::Constant<'4'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_5, ui::DomCode::DIGIT5, ui::EF_NONE,
ui::DomKey::Constant<'5'>::Character},
{ui::VKEY_5, ui::DomCode::DIGIT5, ui::EF_NONE,
ui::DomKey::Constant<'5'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_6, ui::DomCode::DIGIT6, ui::EF_NONE,
ui::DomKey::Constant<'6'>::Character},
{ui::VKEY_6, ui::DomCode::DIGIT6, ui::EF_NONE,
ui::DomKey::Constant<'6'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_7, ui::DomCode::DIGIT7, ui::EF_NONE,
ui::DomKey::Constant<'7'>::Character},
{ui::VKEY_7, ui::DomCode::DIGIT7, ui::EF_NONE,
ui::DomKey::Constant<'7'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_8, ui::DomCode::DIGIT8, ui::EF_NONE,
ui::DomKey::Constant<'8'>::Character},
{ui::VKEY_8, ui::DomCode::DIGIT8, ui::EF_NONE,
ui::DomKey::Constant<'8'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_9, ui::DomCode::DIGIT9, ui::EF_NONE,
ui::DomKey::Constant<'9'>::Character},
{ui::VKEY_9, ui::DomCode::DIGIT9, ui::EF_NONE,
ui::DomKey::Constant<'9'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_0, ui::DomCode::DIGIT0, ui::EF_NONE,
ui::DomKey::Constant<'0'>::Character},
{ui::VKEY_0, ui::DomCode::DIGIT0, ui::EF_NONE,
ui::DomKey::Constant<'0'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_MINUS, ui::DomCode::MINUS, ui::EF_NONE,
ui::DomKey::Constant<'-'>::Character},
{ui::VKEY_OEM_MINUS, ui::DomCode::MINUS, ui::EF_NONE,
ui::DomKey::Constant<'-'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PLUS, ui::DomCode::EQUAL, ui::EF_NONE,
ui::DomKey::Constant<'='>::Character},
{ui::VKEY_OEM_PLUS, ui::DomCode::EQUAL, ui::EF_NONE,
ui::DomKey::Constant<'='>::Character}},
});
}
// TODO(crbug.com/1179893): Remove once the feature is enabled permanently.
TEST_F(EventRewriterTest, TestRewriteSearchNumberToFunctionKeyOld) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
scoped_feature_list_.InitAndDisableFeature(
::features::kImprovedKeyboardShortcuts);
TestNonAppleNonCustomLayoutKeyboardVariants({
// The number row should be rewritten as the F<number> row with Search
// key.
{ui::ET_KEY_PRESSED,
{ui::VKEY_1, ui::DomCode::DIGIT1, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'1'>::Character},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_2, ui::DomCode::DIGIT2, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'2'>::Character},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_3, ui::DomCode::DIGIT3, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'3'>::Character},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_4, ui::DomCode::DIGIT4, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'4'>::Character},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_5, ui::DomCode::DIGIT5, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'5'>::Character},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_6, ui::DomCode::DIGIT6, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'6'>::Character},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_7, ui::DomCode::DIGIT7, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'7'>::Character},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_8, ui::DomCode::DIGIT8, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'8'>::Character},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_9, ui::DomCode::DIGIT9, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'9'>::Character},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_0, ui::DomCode::DIGIT0, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'0'>::Character},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_MINUS, ui::DomCode::MINUS, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'-'>::Character},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PLUS, ui::DomCode::EQUAL, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'='>::Character},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
});
}
TEST_F(EventRewriterTest, TestRewriteSearchNumberToFunctionKeyNoAction) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
TestNonAppleNonCustomLayoutKeyboardVariants({
// Search+Number should now have no effect but a notification will
// be shown the first time F1 to F10 is pressed.
{ui::ET_KEY_PRESSED,
{ui::VKEY_1, ui::DomCode::DIGIT1, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'1'>::Character},
{ui::VKEY_1, ui::DomCode::DIGIT1, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'1'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
{ui::ET_KEY_PRESSED,
{ui::VKEY_2, ui::DomCode::DIGIT2, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'2'>::Character},
{ui::VKEY_2, ui::DomCode::DIGIT2, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'2'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
{ui::ET_KEY_PRESSED,
{ui::VKEY_3, ui::DomCode::DIGIT3, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'3'>::Character},
{ui::VKEY_3, ui::DomCode::DIGIT3, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'3'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
{ui::ET_KEY_PRESSED,
{ui::VKEY_4, ui::DomCode::DIGIT4, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'4'>::Character},
{ui::VKEY_4, ui::DomCode::DIGIT4, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'4'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
{ui::ET_KEY_PRESSED,
{ui::VKEY_5, ui::DomCode::DIGIT5, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'5'>::Character},
{ui::VKEY_5, ui::DomCode::DIGIT5, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'5'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
{ui::ET_KEY_PRESSED,
{ui::VKEY_6, ui::DomCode::DIGIT6, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'6'>::Character},
{ui::VKEY_6, ui::DomCode::DIGIT6, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'6'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
{ui::ET_KEY_PRESSED,
{ui::VKEY_7, ui::DomCode::DIGIT7, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'7'>::Character},
{ui::VKEY_7, ui::DomCode::DIGIT7, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'7'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
{ui::ET_KEY_PRESSED,
{ui::VKEY_8, ui::DomCode::DIGIT8, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'8'>::Character},
{ui::VKEY_8, ui::DomCode::DIGIT8, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'8'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
{ui::ET_KEY_PRESSED,
{ui::VKEY_9, ui::DomCode::DIGIT9, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'9'>::Character},
{ui::VKEY_9, ui::DomCode::DIGIT9, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'9'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
{ui::ET_KEY_PRESSED,
{ui::VKEY_0, ui::DomCode::DIGIT0, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'0'>::Character},
{ui::VKEY_0, ui::DomCode::DIGIT0, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'0'>::Character},
kKeyboardDeviceId,
/*triggers_notification=*/true},
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_MINUS, ui::DomCode::MINUS, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'-'>::Character},
{ui::VKEY_OEM_MINUS, ui::DomCode::MINUS, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'-'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PLUS, ui::DomCode::EQUAL, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'='>::Character},
{ui::VKEY_OEM_PLUS, ui::DomCode::EQUAL, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'='>::Character}},
});
}
TEST_F(EventRewriterTest, TestFunctionKeysNotRewrittenBySearch) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
TestNonAppleNonCustomLayoutKeyboardVariants({
// The function keys should not be rewritten with Search key pressed.
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_COMMAND_DOWN, ui::DomKey::F1},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_COMMAND_DOWN, ui::DomKey::F2},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_COMMAND_DOWN, ui::DomKey::F3},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_COMMAND_DOWN, ui::DomKey::F4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_COMMAND_DOWN, ui::DomKey::F5},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_COMMAND_DOWN, ui::DomKey::F6},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_COMMAND_DOWN, ui::DomKey::F7},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_COMMAND_DOWN, ui::DomKey::F8},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_COMMAND_DOWN, ui::DomKey::F9},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_COMMAND_DOWN, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_COMMAND_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_COMMAND_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
});
}
TEST_F(EventRewriterTest, TestRewriteFunctionKeysNonCustomLayouts) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
// Old CrOS keyboards that do not have custom layouts send F-Keys by default
// and are translated by default to Actions based on hardcoded mappings.
// New CrOS keyboards are not tested here because they do not remap F-Keys.
TestNonAppleNonCustomLayoutKeyboardVariants({
// F1 -> Back
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1},
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_NONE,
ui::DomKey::BROWSER_BACK}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_CONTROL_DOWN, ui::DomKey::F1},
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_CONTROL_DOWN,
ui::DomKey::BROWSER_BACK}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_ALT_DOWN, ui::DomKey::F1},
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_ALT_DOWN,
ui::DomKey::BROWSER_BACK}},
// F2 -> Forward
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2},
{ui::VKEY_BROWSER_FORWARD, ui::DomCode::BROWSER_FORWARD, ui::EF_NONE,
ui::DomKey::BROWSER_FORWARD}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_CONTROL_DOWN, ui::DomKey::F2},
{ui::VKEY_BROWSER_FORWARD, ui::DomCode::BROWSER_FORWARD,
ui::EF_CONTROL_DOWN, ui::DomKey::BROWSER_FORWARD}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_ALT_DOWN, ui::DomKey::F2},
{ui::VKEY_BROWSER_FORWARD, ui::DomCode::BROWSER_FORWARD, ui::EF_ALT_DOWN,
ui::DomKey::BROWSER_FORWARD}},
// F3 -> Refresh
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_CONTROL_DOWN, ui::DomKey::F3},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH,
ui::EF_CONTROL_DOWN, ui::DomKey::BROWSER_REFRESH}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_ALT_DOWN, ui::DomKey::F3},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_ALT_DOWN,
ui::DomKey::BROWSER_REFRESH}},
// F4 -> Zoom (aka Fullscreen)
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_CONTROL_DOWN, ui::DomKey::F4},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_CONTROL_DOWN,
ui::DomKey::ZOOM_TOGGLE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_ALT_DOWN, ui::DomKey::F4},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_ALT_DOWN,
ui::DomKey::ZOOM_TOGGLE}},
// F5 -> Launch App 1
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::SELECT_TASK, ui::EF_NONE,
ui::DomKey::LAUNCH_MY_COMPUTER}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_CONTROL_DOWN, ui::DomKey::F5},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::SELECT_TASK,
ui::EF_CONTROL_DOWN, ui::DomKey::LAUNCH_MY_COMPUTER}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_ALT_DOWN, ui::DomKey::F5},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::SELECT_TASK, ui::EF_ALT_DOWN,
ui::DomKey::LAUNCH_MY_COMPUTER}},
// F6 -> Brightness down
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6},
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_CONTROL_DOWN, ui::DomKey::F6},
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN,
ui::EF_CONTROL_DOWN, ui::DomKey::BRIGHTNESS_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_ALT_DOWN, ui::DomKey::F6},
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN, ui::EF_ALT_DOWN,
ui::DomKey::BRIGHTNESS_DOWN}},
// F7 -> Brightness up
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_CONTROL_DOWN, ui::DomKey::F7},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_CONTROL_DOWN,
ui::DomKey::BRIGHTNESS_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_ALT_DOWN, ui::DomKey::F7},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_ALT_DOWN,
ui::DomKey::BRIGHTNESS_UP}},
// F8 -> Volume Mute
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8},
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_MUTE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_CONTROL_DOWN, ui::DomKey::F8},
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_CONTROL_DOWN,
ui::DomKey::AUDIO_VOLUME_MUTE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_ALT_DOWN, ui::DomKey::F8},
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_ALT_DOWN,
ui::DomKey::AUDIO_VOLUME_MUTE}},
// F9 -> Volume Down
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9},
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_CONTROL_DOWN, ui::DomKey::F9},
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_CONTROL_DOWN,
ui::DomKey::AUDIO_VOLUME_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_ALT_DOWN, ui::DomKey::F9},
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_ALT_DOWN,
ui::DomKey::AUDIO_VOLUME_DOWN}},
// F10 -> Volume Up
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10},
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_CONTROL_DOWN, ui::DomKey::F10},
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_CONTROL_DOWN,
ui::DomKey::AUDIO_VOLUME_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_ALT_DOWN, ui::DomKey::F10},
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_ALT_DOWN,
ui::DomKey::AUDIO_VOLUME_UP}},
// F11 -> F11
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_CONTROL_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_CONTROL_DOWN, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_ALT_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_ALT_DOWN, ui::DomKey::F11}},
// F12 -> F12
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_CONTROL_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_CONTROL_DOWN, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_ALT_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_ALT_DOWN, ui::DomKey::F12}},
});
}
TEST_F(EventRewriterTest, TestRewriteFunctionKeysCustomLayoutsFKeyUnchanged) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
// On devices with custom layouts, the F-Keys are never remapped.
TestChromeCustomLayoutKeyboardVariants({
// F1-> F1
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_CONTROL_DOWN, ui::DomKey::F1},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_CONTROL_DOWN, ui::DomKey::F1}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_ALT_DOWN, ui::DomKey::F1},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_ALT_DOWN, ui::DomKey::F1}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_COMMAND_DOWN, ui::DomKey::F1},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_COMMAND_DOWN, ui::DomKey::F1}},
// F2 -> F2
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_CONTROL_DOWN, ui::DomKey::F2},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_CONTROL_DOWN, ui::DomKey::F2}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_ALT_DOWN, ui::DomKey::F2},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_ALT_DOWN, ui::DomKey::F2}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_COMMAND_DOWN, ui::DomKey::F2},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_COMMAND_DOWN, ui::DomKey::F2}},
// F3 -> F3
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_CONTROL_DOWN, ui::DomKey::F3},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_CONTROL_DOWN, ui::DomKey::F3}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_ALT_DOWN, ui::DomKey::F3},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_ALT_DOWN, ui::DomKey::F3}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_COMMAND_DOWN, ui::DomKey::F3},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_COMMAND_DOWN, ui::DomKey::F3}},
// F4 -> F4
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_CONTROL_DOWN, ui::DomKey::F4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_CONTROL_DOWN, ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_ALT_DOWN, ui::DomKey::F4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_ALT_DOWN, ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_COMMAND_DOWN, ui::DomKey::F4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_COMMAND_DOWN, ui::DomKey::F4}},
// F5 -> F5
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_CONTROL_DOWN, ui::DomKey::F5},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_CONTROL_DOWN, ui::DomKey::F5}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_ALT_DOWN, ui::DomKey::F5},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_ALT_DOWN, ui::DomKey::F5}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_COMMAND_DOWN, ui::DomKey::F5},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_COMMAND_DOWN, ui::DomKey::F5}},
// F6 -> F6
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_CONTROL_DOWN, ui::DomKey::F6},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_CONTROL_DOWN, ui::DomKey::F6}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_ALT_DOWN, ui::DomKey::F6},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_ALT_DOWN, ui::DomKey::F6}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_COMMAND_DOWN, ui::DomKey::F6},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_COMMAND_DOWN, ui::DomKey::F6}},
// F7 -> F7
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_CONTROL_DOWN, ui::DomKey::F7},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_CONTROL_DOWN, ui::DomKey::F7}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_ALT_DOWN, ui::DomKey::F7},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_ALT_DOWN, ui::DomKey::F7}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_COMMAND_DOWN, ui::DomKey::F7},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_COMMAND_DOWN, ui::DomKey::F7}},
// F8 -> F8
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_CONTROL_DOWN, ui::DomKey::F8},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_CONTROL_DOWN, ui::DomKey::F8}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_ALT_DOWN, ui::DomKey::F8},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_ALT_DOWN, ui::DomKey::F8}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_COMMAND_DOWN, ui::DomKey::F8},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_COMMAND_DOWN, ui::DomKey::F8}},
// F9 -> F9
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_CONTROL_DOWN, ui::DomKey::F9},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_CONTROL_DOWN, ui::DomKey::F9}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_ALT_DOWN, ui::DomKey::F9},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_ALT_DOWN, ui::DomKey::F9}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_COMMAND_DOWN, ui::DomKey::F9},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_COMMAND_DOWN, ui::DomKey::F9}},
// F10 -> F10
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_CONTROL_DOWN, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_CONTROL_DOWN, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_ALT_DOWN, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_ALT_DOWN, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_COMMAND_DOWN, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_COMMAND_DOWN, ui::DomKey::F10}},
// F11 -> F11
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_CONTROL_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_CONTROL_DOWN, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_ALT_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_ALT_DOWN, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_COMMAND_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_COMMAND_DOWN, ui::DomKey::F11}},
// F12 -> F12
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_CONTROL_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_CONTROL_DOWN, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_ALT_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_ALT_DOWN, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_COMMAND_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_COMMAND_DOWN, ui::DomKey::F12}},
// F13 -> F13
{ui::ET_KEY_PRESSED,
{ui::VKEY_F13, ui::DomCode::F13, ui::EF_NONE, ui::DomKey::F13},
{ui::VKEY_F13, ui::DomCode::F13, ui::EF_NONE, ui::DomKey::F13}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F13, ui::DomCode::F13, ui::EF_CONTROL_DOWN, ui::DomKey::F13},
{ui::VKEY_F13, ui::DomCode::F13, ui::EF_CONTROL_DOWN, ui::DomKey::F13}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F13, ui::DomCode::F13, ui::EF_ALT_DOWN, ui::DomKey::F13},
{ui::VKEY_F13, ui::DomCode::F13, ui::EF_ALT_DOWN, ui::DomKey::F13}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F13, ui::DomCode::F13, ui::EF_COMMAND_DOWN, ui::DomKey::F13},
{ui::VKEY_F13, ui::DomCode::F13, ui::EF_COMMAND_DOWN, ui::DomKey::F13}},
// F14 -> F14
{ui::ET_KEY_PRESSED,
{ui::VKEY_F14, ui::DomCode::F14, ui::EF_NONE, ui::DomKey::F14},
{ui::VKEY_F14, ui::DomCode::F14, ui::EF_NONE, ui::DomKey::F14}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F14, ui::DomCode::F14, ui::EF_CONTROL_DOWN, ui::DomKey::F14},
{ui::VKEY_F14, ui::DomCode::F14, ui::EF_CONTROL_DOWN, ui::DomKey::F14}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F14, ui::DomCode::F14, ui::EF_ALT_DOWN, ui::DomKey::F14},
{ui::VKEY_F14, ui::DomCode::F14, ui::EF_ALT_DOWN, ui::DomKey::F14}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F14, ui::DomCode::F14, ui::EF_COMMAND_DOWN, ui::DomKey::F14},
{ui::VKEY_F14, ui::DomCode::F14, ui::EF_COMMAND_DOWN, ui::DomKey::F14}},
// F15 -> F15
{ui::ET_KEY_PRESSED,
{ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15},
{ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F15, ui::DomCode::F15, ui::EF_CONTROL_DOWN, ui::DomKey::F15},
{ui::VKEY_F15, ui::DomCode::F15, ui::EF_CONTROL_DOWN, ui::DomKey::F15}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F15, ui::DomCode::F15, ui::EF_ALT_DOWN, ui::DomKey::F15},
{ui::VKEY_F15, ui::DomCode::F15, ui::EF_ALT_DOWN, ui::DomKey::F15}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F15, ui::DomCode::F15, ui::EF_COMMAND_DOWN, ui::DomKey::F15},
{ui::VKEY_F15, ui::DomCode::F15, ui::EF_COMMAND_DOWN, ui::DomKey::F15}},
});
}
TEST_F(EventRewriterTest, TestRewriteFunctionKeysCustomLayoutsActionUnchanged) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
// An action key on these devices is one where the scan code matches an entry
// in the layout map. It doesn't matter what the action is, as long the
// search key isn't pressed it will pass through unchanged.
const std::string layout = "a1 a2 a3";
TestKeyboard("Internal Custom Layout Keyboard", layout,
ui::INPUT_DEVICE_INTERNAL, /*has_custom_top_row=*/true,
{
{ui::ET_KEY_PRESSED,
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH,
ui::EF_NONE, ui::DomKey::BROWSER_REFRESH, 0xa1},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH,
ui::EF_NONE, ui::DomKey::BROWSER_REFRESH, 0xa1}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_UP, 0xa2},
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_UP, 0xa2}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN,
ui::EF_NONE, ui::DomKey::AUDIO_VOLUME_DOWN, 0xa3},
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN,
ui::EF_NONE, ui::DomKey::AUDIO_VOLUME_DOWN, 0xa3}},
});
}
TEST_F(EventRewriterTest, TestRewriteFunctionKeysCustomLayouts) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
// On devices with custom layouts, scan codes that match the layout
// map get mapped to F-Keys based only on the scan code. The search
// key also gets treated as unpressed in the remapped event.
const std::string layout = "a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af";
TestKeyboard(
"Internal Custom Layout Keyboard", layout, ui::INPUT_DEVICE_INTERNAL,
/*has_custom_top_row=*/true,
{
// Action -> F1
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xa1},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1, 0xa1}},
// Action -> F2
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xa2},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2, 0xa2}},
// Action -> F3
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xa3},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3, 0xa3}},
// Action -> F4
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xa4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4, 0xa4}},
// Action -> F5
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xa5},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5, 0xa5}},
// Action -> F6
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xa6},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6, 0xa6}},
// Action -> F7
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xa7},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7, 0xa7}},
// Action -> F8
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xa8},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8, 0xa8}},
// Action -> F9
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xa9},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9, 0xa9}},
// Action -> F10
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xaa},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10,
0xaa}},
// Action -> F11
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xab},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11,
0xab}},
// Action -> F12
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xac},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12,
0xac}},
// Action -> F13
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xad},
{ui::VKEY_F13, ui::DomCode::F13, ui::EF_NONE, ui::DomKey::F13,
0xad}},
// Action -> F14
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xae},
{ui::VKEY_F14, ui::DomCode::F14, ui::EF_NONE, ui::DomKey::F14,
0xae}},
// Action -> F15
{ui::ET_KEY_PRESSED,
{ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_COMMAND_DOWN,
ui::DomKey::NONE, 0xaf},
{ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15,
0xaf}},
});
}
TEST_F(EventRewriterTest, TestRewriteFunctionKeysLayout2) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
TestKeyboard(
"Internal Keyboard", kKbdTopRowLayout2Tag, ui::INPUT_DEVICE_INTERNAL,
/*has_custom_top_row=*/false,
{
// F1 -> Back
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1},
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_NONE,
ui::DomKey::BROWSER_BACK}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_CONTROL_DOWN, ui::DomKey::F1},
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK,
ui::EF_CONTROL_DOWN, ui::DomKey::BROWSER_BACK}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_ALT_DOWN, ui::DomKey::F1},
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_ALT_DOWN,
ui::DomKey::BROWSER_BACK}},
// F2 -> Refresh
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_CONTROL_DOWN, ui::DomKey::F2},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH,
ui::EF_CONTROL_DOWN, ui::DomKey::BROWSER_REFRESH}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_ALT_DOWN, ui::DomKey::F2},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH,
ui::EF_ALT_DOWN, ui::DomKey::BROWSER_REFRESH}},
// F3 -> Zoom (aka Fullscreen)
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_CONTROL_DOWN, ui::DomKey::F3},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_CONTROL_DOWN,
ui::DomKey::ZOOM_TOGGLE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_ALT_DOWN, ui::DomKey::F3},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_ALT_DOWN,
ui::DomKey::ZOOM_TOGGLE}},
// F4 -> Launch App 1
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::SELECT_TASK, ui::EF_NONE,
ui::DomKey::LAUNCH_MY_COMPUTER}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_CONTROL_DOWN, ui::DomKey::F4},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::SELECT_TASK,
ui::EF_CONTROL_DOWN, ui::DomKey::LAUNCH_MY_COMPUTER}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_ALT_DOWN, ui::DomKey::F4},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::SELECT_TASK,
ui::EF_ALT_DOWN, ui::DomKey::LAUNCH_MY_COMPUTER}},
// F5 -> Brightness down
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5},
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_CONTROL_DOWN, ui::DomKey::F5},
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN,
ui::EF_CONTROL_DOWN, ui::DomKey::BRIGHTNESS_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_ALT_DOWN, ui::DomKey::F5},
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN,
ui::EF_ALT_DOWN, ui::DomKey::BRIGHTNESS_DOWN}},
// F6 -> Brightness up
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_CONTROL_DOWN, ui::DomKey::F6},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP,
ui::EF_CONTROL_DOWN, ui::DomKey::BRIGHTNESS_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_ALT_DOWN, ui::DomKey::F6},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_ALT_DOWN,
ui::DomKey::BRIGHTNESS_UP}},
// F7 -> Media Play/Pause
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7},
{ui::VKEY_MEDIA_PLAY_PAUSE, ui::DomCode::MEDIA_PLAY_PAUSE,
ui::EF_NONE, ui::DomKey::MEDIA_PLAY_PAUSE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_CONTROL_DOWN, ui::DomKey::F7},
{ui::VKEY_MEDIA_PLAY_PAUSE, ui::DomCode::MEDIA_PLAY_PAUSE,
ui::EF_CONTROL_DOWN, ui::DomKey::MEDIA_PLAY_PAUSE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_ALT_DOWN, ui::DomKey::F7},
{ui::VKEY_MEDIA_PLAY_PAUSE, ui::DomCode::MEDIA_PLAY_PAUSE,
ui::EF_ALT_DOWN, ui::DomKey::MEDIA_PLAY_PAUSE}},
// F8 -> Volume Mute
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8},
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_MUTE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_CONTROL_DOWN, ui::DomKey::F8},
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_CONTROL_DOWN,
ui::DomKey::AUDIO_VOLUME_MUTE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_ALT_DOWN, ui::DomKey::F8},
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_ALT_DOWN,
ui::DomKey::AUDIO_VOLUME_MUTE}},
// F9 -> Volume Down
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9},
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_CONTROL_DOWN, ui::DomKey::F9},
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_CONTROL_DOWN,
ui::DomKey::AUDIO_VOLUME_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_ALT_DOWN, ui::DomKey::F9},
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_ALT_DOWN,
ui::DomKey::AUDIO_VOLUME_DOWN}},
// F10 -> Volume Up
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10},
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_CONTROL_DOWN,
ui::DomKey::F10},
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_CONTROL_DOWN,
ui::DomKey::AUDIO_VOLUME_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_ALT_DOWN, ui::DomKey::F10},
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_ALT_DOWN,
ui::DomKey::AUDIO_VOLUME_UP}},
// F11 -> F11
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_CONTROL_DOWN,
ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_CONTROL_DOWN,
ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_ALT_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_ALT_DOWN, ui::DomKey::F11}},
// F12 -> F12
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_CONTROL_DOWN,
ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_CONTROL_DOWN,
ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_ALT_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_ALT_DOWN, ui::DomKey::F12}},
});
}
// TODO(crbug.com/1179893): Remove once the feature is enabled permanently.
TEST_F(EventRewriterTest, TestRewriteFunctionKeysWilcoLayoutsDeprecated) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
scoped_feature_list_.InitAndDisableFeature(
::features::kImprovedKeyboardShortcuts);
std::vector<KeyTestCase> wilco_standard_tests({
// The number row should be rewritten as the F<number> row with Search
// key.
{ui::ET_KEY_PRESSED,
{ui::VKEY_1, ui::DomCode::DIGIT1, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'1'>::Character},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_2, ui::DomCode::DIGIT2, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'2'>::Character},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_3, ui::DomCode::DIGIT3, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'3'>::Character},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_4, ui::DomCode::DIGIT4, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'4'>::Character},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_5, ui::DomCode::DIGIT5, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'5'>::Character},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_6, ui::DomCode::DIGIT6, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'6'>::Character},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_7, ui::DomCode::DIGIT7, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'7'>::Character},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_8, ui::DomCode::DIGIT8, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'8'>::Character},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_9, ui::DomCode::DIGIT9, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'9'>::Character},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_0, ui::DomCode::DIGIT0, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'0'>::Character},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_MINUS, ui::DomCode::MINUS, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'-'>::Character},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PLUS, ui::DomCode::EQUAL, ui::EF_COMMAND_DOWN,
ui::DomKey::Constant<'='>::Character},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
});
}
TEST_F(EventRewriterTest, TestRewriteFunctionKeysWilcoLayouts) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
std::vector<KeyTestCase> wilco_standard_tests({
// F1 -> F1, Search + F1 -> Back
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_COMMAND_DOWN, ui::DomKey::F1},
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_NONE,
ui::DomKey::BROWSER_BACK}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_CONTROL_DOWN, ui::DomKey::F1},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_CONTROL_DOWN, ui::DomKey::F1}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_ALT_DOWN, ui::DomKey::F1},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_ALT_DOWN, ui::DomKey::F1}},
// F2 -> F2, Search + F2 -> Refresh
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_COMMAND_DOWN, ui::DomKey::F2},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_CONTROL_DOWN, ui::DomKey::F2},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_CONTROL_DOWN, ui::DomKey::F2}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_ALT_DOWN, ui::DomKey::F2},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_ALT_DOWN, ui::DomKey::F2}},
// F3 -> F3, Search + F3 -> Zoom (aka Fullscreen)
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_COMMAND_DOWN, ui::DomKey::F3},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_CONTROL_DOWN, ui::DomKey::F3},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_CONTROL_DOWN, ui::DomKey::F3}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_ALT_DOWN, ui::DomKey::F3},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_ALT_DOWN, ui::DomKey::F3}},
// F4 -> F4, Search + F4 -> Launch App 1
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_COMMAND_DOWN, ui::DomKey::F4},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::F4, ui::EF_NONE,
ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_CONTROL_DOWN, ui::DomKey::F4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_CONTROL_DOWN, ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_ALT_DOWN, ui::DomKey::F4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_ALT_DOWN, ui::DomKey::F4}},
// F5 -> F5, Search + F5 -> Brightness down
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_COMMAND_DOWN, ui::DomKey::F5},
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_CONTROL_DOWN, ui::DomKey::F5},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_CONTROL_DOWN, ui::DomKey::F5}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_ALT_DOWN, ui::DomKey::F5},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_ALT_DOWN, ui::DomKey::F5}},
// F6 -> F6, Search + F6 -> Brightness up
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_COMMAND_DOWN, ui::DomKey::F6},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_CONTROL_DOWN, ui::DomKey::F6},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_CONTROL_DOWN, ui::DomKey::F6}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_ALT_DOWN, ui::DomKey::F6},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_ALT_DOWN, ui::DomKey::F6}},
// F7 -> F7, Search + F7 -> Volume mute
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_COMMAND_DOWN, ui::DomKey::F7},
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_MUTE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_CONTROL_DOWN, ui::DomKey::F7},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_CONTROL_DOWN, ui::DomKey::F7}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_ALT_DOWN, ui::DomKey::F7},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_ALT_DOWN, ui::DomKey::F7}},
// F8 -> F8, Search + F8 -> Volume Down
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_COMMAND_DOWN, ui::DomKey::F8},
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_CONTROL_DOWN, ui::DomKey::F8},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_CONTROL_DOWN, ui::DomKey::F8}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_ALT_DOWN, ui::DomKey::F8},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_ALT_DOWN, ui::DomKey::F8}},
// F9 -> F9, Search + F9 -> Volume Up
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_COMMAND_DOWN, ui::DomKey::F9},
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_CONTROL_DOWN, ui::DomKey::F9},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_CONTROL_DOWN, ui::DomKey::F9}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_ALT_DOWN, ui::DomKey::F9},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_ALT_DOWN, ui::DomKey::F9}},
// F10 -> F10, Search + F10 -> F10
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_COMMAND_DOWN, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_CONTROL_DOWN, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_CONTROL_DOWN, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_ALT_DOWN, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_ALT_DOWN, ui::DomKey::F10}},
// F11 -> F11, Search + F11 -> F11
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_COMMAND_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_CONTROL_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_CONTROL_DOWN, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_ALT_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_ALT_DOWN, ui::DomKey::F11}},
// F12 -> F12
// Search + F12 differs between Wilco devices so it is tested separately.
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_CONTROL_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_CONTROL_DOWN, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_ALT_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_ALT_DOWN, ui::DomKey::F12}},
// The number row should not be rewritten without Search key.
{ui::ET_KEY_PRESSED,
{ui::VKEY_1, ui::DomCode::DIGIT1, ui::EF_NONE,
ui::DomKey::Constant<'1'>::Character},
{ui::VKEY_1, ui::DomCode::DIGIT1, ui::EF_NONE,
ui::DomKey::Constant<'1'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_2, ui::DomCode::DIGIT2, ui::EF_NONE,
ui::DomKey::Constant<'2'>::Character},
{ui::VKEY_2, ui::DomCode::DIGIT2, ui::EF_NONE,
ui::DomKey::Constant<'2'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_3, ui::DomCode::DIGIT3, ui::EF_NONE,
ui::DomKey::Constant<'3'>::Character},
{ui::VKEY_3, ui::DomCode::DIGIT3, ui::EF_NONE,
ui::DomKey::Constant<'3'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_4, ui::DomCode::DIGIT4, ui::EF_NONE,
ui::DomKey::Constant<'4'>::Character},
{ui::VKEY_4, ui::DomCode::DIGIT4, ui::EF_NONE,
ui::DomKey::Constant<'4'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_5, ui::DomCode::DIGIT5, ui::EF_NONE,
ui::DomKey::Constant<'5'>::Character},
{ui::VKEY_5, ui::DomCode::DIGIT5, ui::EF_NONE,
ui::DomKey::Constant<'5'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_6, ui::DomCode::DIGIT6, ui::EF_NONE,
ui::DomKey::Constant<'6'>::Character},
{ui::VKEY_6, ui::DomCode::DIGIT6, ui::EF_NONE,
ui::DomKey::Constant<'6'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_7, ui::DomCode::DIGIT7, ui::EF_NONE,
ui::DomKey::Constant<'7'>::Character},
{ui::VKEY_7, ui::DomCode::DIGIT7, ui::EF_NONE,
ui::DomKey::Constant<'7'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_8, ui::DomCode::DIGIT8, ui::EF_NONE,
ui::DomKey::Constant<'8'>::Character},
{ui::VKEY_8, ui::DomCode::DIGIT8, ui::EF_NONE,
ui::DomKey::Constant<'8'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_9, ui::DomCode::DIGIT9, ui::EF_NONE,
ui::DomKey::Constant<'9'>::Character},
{ui::VKEY_9, ui::DomCode::DIGIT9, ui::EF_NONE,
ui::DomKey::Constant<'9'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_0, ui::DomCode::DIGIT0, ui::EF_NONE,
ui::DomKey::Constant<'0'>::Character},
{ui::VKEY_0, ui::DomCode::DIGIT0, ui::EF_NONE,
ui::DomKey::Constant<'0'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_MINUS, ui::DomCode::MINUS, ui::EF_NONE,
ui::DomKey::Constant<'-'>::Character},
{ui::VKEY_OEM_MINUS, ui::DomCode::MINUS, ui::EF_NONE,
ui::DomKey::Constant<'-'>::Character}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_OEM_PLUS, ui::DomCode::EQUAL, ui::EF_NONE,
ui::DomKey::Constant<'='>::Character},
{ui::VKEY_OEM_PLUS, ui::DomCode::EQUAL, ui::EF_NONE,
ui::DomKey::Constant<'='>::Character}},
});
KeyTestCase wilco_1_test =
// Search + F12 -> Ctrl + Zoom (aka Fullscreen) (Display toggle)
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_COMMAND_DOWN, ui::DomKey::F12},
{ui::VKEY_ZOOM, ui::DomCode::F12, ui::EF_CONTROL_DOWN, ui::DomKey::F12}};
KeyTestCase drallion_test_no_privacy_screen =
// Search + F12 -> F12 (Privacy screen not supported)
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_COMMAND_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}};
KeyTestCase drallion_test_privacy_screen =
// F12 -> F12, Search + F12 -> Privacy Screen Toggle
{ui::ET_KEY_PRESSED,
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_COMMAND_DOWN, ui::DomKey::F12},
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_NONE, ui::DomKey::F12}};
// Set keyboard layout to Wilco 1.0
SetupKeyboard("Wilco Keyboard", kKbdTopRowLayoutWilcoTag);
// Standard key tests using Wilco 1.0 keyboard
for (const auto& test : wilco_standard_tests)
CheckKeyTestCase(rewriter(), test);
CheckKeyTestCase(rewriter(), wilco_1_test);
// Set keyboard layout to Drallion (Wilco 1.5)
SetupKeyboard("Drallion Keyboard", kKbdTopRowLayoutDrallionTag);
// Run key tests using Drallion keyboard layout (no privacy screen)
rewriter_->set_privacy_screen_for_testing(false);
for (const auto& test : wilco_standard_tests)
CheckKeyTestCase(rewriter(), test);
CheckKeyTestCase(rewriter(), drallion_test_no_privacy_screen);
// Run key tests using Drallion keyboard layout (privacy screen supported)
rewriter_->set_privacy_screen_for_testing(true);
for (const auto& test : wilco_standard_tests)
CheckKeyTestCase(rewriter(), test);
CheckKeyTestCase(rewriter(), drallion_test_privacy_screen);
}
TEST_F(EventRewriterTest, TestRewriteActionKeysWilcoLayouts) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
KeyTestCase wilco_standard_tests[] = {
// Back -> Back, Search + Back -> F1
{ui::ET_KEY_PRESSED,
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_NONE,
ui::DomKey::BROWSER_BACK},
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_NONE,
ui::DomKey::BROWSER_BACK}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_COMMAND_DOWN,
ui::DomKey::BROWSER_BACK},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1}},
// Refresh -> Refresh, Search + Refresh -> F2
{ui::ET_KEY_PRESSED,
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH,
ui::EF_COMMAND_DOWN, ui::DomKey::BROWSER_REFRESH},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2}},
// Full Screen -> Full Screen, Search + Full Screen -> F3
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_COMMAND_DOWN,
ui::DomKey::ZOOM_TOGGLE},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3}},
// Launch App 1 -> Launch App 1, Search + Launch App 1 -> F4
{ui::ET_KEY_PRESSED,
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::F4, ui::EF_NONE,
ui::DomKey::F4},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::F4, ui::EF_NONE,
ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::F4, ui::EF_COMMAND_DOWN,
ui::DomKey::F4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4}},
// Brightness down -> Brightness Down, Search Brightness Down -> F5
{ui::ET_KEY_PRESSED,
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_DOWN},
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN,
ui::EF_COMMAND_DOWN, ui::DomKey::BRIGHTNESS_DOWN},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5}},
// Brightness up -> Brightness Up, Search + Brightness Up -> F6
{ui::ET_KEY_PRESSED,
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_UP},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_COMMAND_DOWN,
ui::DomKey::BRIGHTNESS_UP},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6}},
// Volume mute -> Volume Mute, Search + Volume Mute -> F7
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_MUTE},
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_MUTE}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_COMMAND_DOWN,
ui::DomKey::AUDIO_VOLUME_MUTE},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7}},
// Volume Down -> Volume Down, Search + Volume Down -> F8
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_DOWN},
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_DOWN}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_COMMAND_DOWN,
ui::DomKey::AUDIO_VOLUME_DOWN},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8}},
// Volume Up -> Volume Up, Search + Volume Up -> F9
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_UP},
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_UP}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_COMMAND_DOWN,
ui::DomKey::AUDIO_VOLUME_UP},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9}},
// F10 -> F10
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_COMMAND_DOWN, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10}},
// F11 -> F11
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_COMMAND_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}}};
KeyTestCase wilco_1_tests[] = {
// Ctrl + Zoom (Display toggle) -> Unchanged
// Search + Ctrl + Zoom (Display toggle) -> F12
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::F12, ui::EF_CONTROL_DOWN, ui::DomKey::F12},
{ui::VKEY_ZOOM, ui::DomCode::F12, ui::EF_CONTROL_DOWN, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::F12,
ui::EF_COMMAND_DOWN + ui::EF_CONTROL_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}}};
KeyTestCase drallion_tests_no_privacy_screen[] = {
// Privacy Screen Toggle -> F12 (Privacy Screen not supported),
// Search + Privacy Screen Toggle -> F12
{ui::ET_KEY_PRESSED,
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_NONE, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_COMMAND_DOWN, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
// Ctrl + Zoom (Display toggle) -> Unchanged
// Search + Ctrl + Zoom (Display toggle) -> Unchanged
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::NONE, ui::EF_CONTROL_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_ZOOM, ui::DomCode::NONE, ui::EF_CONTROL_DOWN,
ui::DomKey::UNIDENTIFIED}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::NONE,
ui::EF_COMMAND_DOWN + ui::EF_CONTROL_DOWN, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_ZOOM, ui::DomCode::NONE, ui::EF_CONTROL_DOWN,
ui::DomKey::UNIDENTIFIED}}};
KeyTestCase drallion_tests_privacy_screen[] = {
// Privacy Screen Toggle -> Privacy Screen Toggle,
// Search + Privacy Screen Toggle -> F12
{ui::ET_KEY_PRESSED,
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_NONE, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_NONE, ui::DomKey::UNIDENTIFIED}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_COMMAND_DOWN, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
// Ctrl + Zoom (Display toggle) -> Unchanged
// Search + Ctrl + Zoom (Display toggle) -> Unchanged
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::NONE, ui::EF_CONTROL_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_ZOOM, ui::DomCode::NONE, ui::EF_CONTROL_DOWN,
ui::DomKey::UNIDENTIFIED}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::NONE,
ui::EF_COMMAND_DOWN + ui::EF_CONTROL_DOWN, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_ZOOM, ui::DomCode::NONE, ui::EF_CONTROL_DOWN,
ui::DomKey::UNIDENTIFIED}}};
// Set keyboard layout to Wilco 1.0
SetupKeyboard("Wilco Keyboard", kKbdTopRowLayoutWilcoTag);
// Standard key tests using Wilco 1.0 keyboard
for (const auto& test : wilco_standard_tests)
CheckKeyTestCase(rewriter(), test);
// Wilco 1.0 specific key tests
for (const auto& test : wilco_1_tests)
CheckKeyTestCase(rewriter(), test);
// Set keyboard layout to Drallion (Wilco 1.5)
SetupKeyboard("Drallion Keyboard", kKbdTopRowLayoutDrallionTag);
// Standard key tests using Drallion keyboard layout
for (const auto& test : wilco_standard_tests)
CheckKeyTestCase(rewriter(), test);
// Drallion specific key tests (no privacy screen)
rewriter_->set_privacy_screen_for_testing(false);
for (const auto& test : drallion_tests_no_privacy_screen)
CheckKeyTestCase(rewriter(), test);
// Drallion specific key tests (privacy screen supported)
rewriter_->set_privacy_screen_for_testing(true);
for (const auto& test : drallion_tests_privacy_screen)
CheckKeyTestCase(rewriter(), test);
}
TEST_F(EventRewriterTest, TestTopRowAsFnKeysForKeyboardWilcoLayouts) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
// Enable preference treat-top-row-as-function-keys.
// That causes action keys to be mapped back to Fn keys, unless the search
// key is pressed.
BooleanPrefMember top_row_as_fn_key;
top_row_as_fn_key.Init(prefs::kLanguageSendFunctionKeys, prefs());
top_row_as_fn_key.SetValue(true);
KeyTestCase wilco_standard_tests[] = {
// Back -> F1, Search + Back -> Back
{ui::ET_KEY_PRESSED,
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_NONE,
ui::DomKey::BROWSER_BACK},
{ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_COMMAND_DOWN,
ui::DomKey::BROWSER_BACK},
{ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_NONE,
ui::DomKey::BROWSER_BACK}},
// Refresh -> F2, Search + Refresh -> Refresh
{ui::ET_KEY_PRESSED,
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH},
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH,
ui::EF_COMMAND_DOWN, ui::DomKey::BROWSER_REFRESH},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH}},
// Full Screen -> F3, Search + Full Screen -> Full Screen
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE},
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_COMMAND_DOWN,
ui::DomKey::ZOOM_TOGGLE},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE}},
// Launch App 1 -> F4, Search + Launch App 1 -> Launch App 1
{ui::ET_KEY_PRESSED,
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::F4, ui::EF_NONE,
ui::DomKey::F4},
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::F4, ui::EF_COMMAND_DOWN,
ui::DomKey::F4},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::F4, ui::EF_NONE,
ui::DomKey::F4}},
// Brightness down -> F5, Search Brightness Down -> Brightness Down
{ui::ET_KEY_PRESSED,
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_DOWN},
{ui::VKEY_F5, ui::DomCode::F5, ui::EF_NONE, ui::DomKey::F5}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN,
ui::EF_COMMAND_DOWN, ui::DomKey::BRIGHTNESS_DOWN},
{ui::VKEY_BRIGHTNESS_DOWN, ui::DomCode::BRIGHTNESS_DOWN, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_DOWN}},
// Brightness up -> F6, Search + Brightness Up -> Brightness Up
{ui::ET_KEY_PRESSED,
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_UP},
{ui::VKEY_F6, ui::DomCode::F6, ui::EF_NONE, ui::DomKey::F6}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_COMMAND_DOWN,
ui::DomKey::BRIGHTNESS_UP},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_UP}},
// Volume mute -> F7, Search + Volume Mute -> Volume Mute
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_MUTE},
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_COMMAND_DOWN,
ui::DomKey::AUDIO_VOLUME_MUTE},
{ui::VKEY_VOLUME_MUTE, ui::DomCode::VOLUME_MUTE, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_MUTE}},
// Volume Down -> F8, Search + Volume Down -> Volume Down
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_DOWN},
{ui::VKEY_F8, ui::DomCode::F8, ui::EF_NONE, ui::DomKey::F8}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_COMMAND_DOWN,
ui::DomKey::AUDIO_VOLUME_DOWN},
{ui::VKEY_VOLUME_DOWN, ui::DomCode::VOLUME_DOWN, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_DOWN}},
// Volume Up -> F9, Search + Volume Up -> Volume Up
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_UP},
{ui::VKEY_F9, ui::DomCode::F9, ui::EF_NONE, ui::DomKey::F9}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_COMMAND_DOWN,
ui::DomKey::AUDIO_VOLUME_UP},
{ui::VKEY_VOLUME_UP, ui::DomCode::VOLUME_UP, ui::EF_NONE,
ui::DomKey::AUDIO_VOLUME_UP}},
// F10 -> F10
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_COMMAND_DOWN, ui::DomKey::F10},
{ui::VKEY_F10, ui::DomCode::F10, ui::EF_NONE, ui::DomKey::F10}},
// F11 -> F11
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_COMMAND_DOWN, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11},
{ui::VKEY_F11, ui::DomCode::F11, ui::EF_NONE, ui::DomKey::F11}}};
KeyTestCase wilco_1_tests[] = {
// Ctrl + Zoom (Display toggle) -> F12
// Search + Ctrl + Zoom (Display toggle) -> Unchanged
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::F12, ui::EF_CONTROL_DOWN, ui::DomKey::F12},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::F12,
ui::EF_COMMAND_DOWN + ui::EF_CONTROL_DOWN, ui::DomKey::F12},
{ui::VKEY_ZOOM, ui::DomCode::F12, ui::EF_CONTROL_DOWN,
ui::DomKey::F12}}};
KeyTestCase drallion_tests_no_privacy_screen[] = {
// Privacy Screen Toggle -> F12,
// Search + Privacy Screen Toggle -> F12 (Privacy screen not supported)
{ui::ET_KEY_PRESSED,
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_NONE, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_COMMAND_DOWN, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
// Ctrl + Zoom (Display toggle) -> Unchanged
// Search + Ctrl + Zoom (Display toggle) -> Unchanged
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::NONE, ui::EF_CONTROL_DOWN,
ui::DomKey::UNIDENTIFIED},
{ui::VKEY_ZOOM, ui::DomCode::NONE, ui::EF_CONTROL_DOWN,
ui::DomKey::UNIDENTIFIED}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_ZOOM, ui::DomCode::NONE,
ui::EF_COMMAND_DOWN + ui::EF_CONTROL_DOWN, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_ZOOM, ui::DomCode::NONE, ui::EF_CONTROL_DOWN,
ui::DomKey::UNIDENTIFIED}}};
KeyTestCase drallion_tests_privacy_screen[] = {
// Privacy Screen Toggle -> F12,
// Search + Privacy Screen Toggle -> Unchanged
{ui::ET_KEY_PRESSED,
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_NONE, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_F12, ui::DomCode::F12, ui::EF_NONE, ui::DomKey::F12}},
{ui::ET_KEY_PRESSED,
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_COMMAND_DOWN, ui::DomKey::UNIDENTIFIED},
{ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::DomCode::PRIVACY_SCREEN_TOGGLE,
ui::EF_NONE, ui::DomKey::UNIDENTIFIED}}};
// Run key test cases for Wilco 1.0 keyboard layout
SetupKeyboard("Wilco Keyboard", kKbdTopRowLayoutWilcoTag);
// Standard key tests using Wilco 1.0 keyboard
for (const auto& test : wilco_standard_tests)
CheckKeyTestCase(rewriter(), test);
// Wilco 1.0 specific key tests
for (const auto& test : wilco_1_tests)
CheckKeyTestCase(rewriter(), test);
// Run key test cases for Drallion (Wilco 1.5) keyboard layout
SetupKeyboard("Drallion Keyboard", kKbdTopRowLayoutDrallionTag);
// Standard key tests using Drallion keyboard layout
for (const auto& test : wilco_standard_tests)
CheckKeyTestCase(rewriter(), test);
// Drallion specific key tests (no privacy screen)
rewriter_->set_privacy_screen_for_testing(false);
for (const auto& test : drallion_tests_no_privacy_screen)
CheckKeyTestCase(rewriter(), test);
// Drallion specific key tests (privacy screen supported)
rewriter_->set_privacy_screen_for_testing(true);
for (const auto& test : drallion_tests_privacy_screen)
CheckKeyTestCase(rewriter(), test);
}
TEST_F(EventRewriterTest, TestRewriteFunctionKeysInvalidLayout) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
// Not adding a keyboard simulates a failure in getting top row layout, which
// will fallback to Layout1 in which case keys are rewritten to their default
// values.
KeyTestCase invalid_layout_tests[] = {
// F2 -> Forward
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2},
{ui::VKEY_BROWSER_FORWARD, ui::DomCode::BROWSER_FORWARD, ui::EF_NONE,
ui::DomKey::BROWSER_FORWARD}},
// F3 -> Refresh
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH}},
// F4 -> Zoom (aka Fullscreen)
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE}},
// F7 -> Brightness up
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_UP}},
};
for (const auto& test : invalid_layout_tests)
CheckKeyTestCase(rewriter(), test);
// Adding a keyboard with a valid layout will take effect.
const std::vector<KeyTestCase> layout2_tests({
// F2 -> Refresh
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH}},
// F3 -> Zoom (aka Fullscreen)
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3},
{ui::VKEY_ZOOM, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE}},
// F4 -> Launch App 1
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::SELECT_TASK, ui::EF_NONE,
ui::DomKey::LAUNCH_MY_COMPUTER}},
// F7 -> Media Play/Pause
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7},
{ui::VKEY_MEDIA_PLAY_PAUSE, ui::DomCode::MEDIA_PLAY_PAUSE, ui::EF_NONE,
ui::DomKey::MEDIA_PLAY_PAUSE}},
});
TestKeyboard("Internal Keyboard", kKbdTopRowLayout2Tag,
ui::INPUT_DEVICE_INTERNAL, /*has_custom_top_row=*/false,
layout2_tests);
}
// Tests that event rewrites still work even if modifiers are remapped.
TEST_F(EventRewriterTest, TestRewriteExtendedKeysWithControlRemapped) {
// Remap Control to Search.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember search;
InitModifierKeyPref(&search, prefs::kLanguageRemapControlKeyTo,
ui::chromeos::ModifierKey::kSearchKey);
TestChromeKeyboardVariants({
// Ctrl+Right -> End
{ui::ET_KEY_PRESSED,
{ui::VKEY_RIGHT, ui::DomCode::ARROW_RIGHT, ui::EF_CONTROL_DOWN,
ui::DomKey::ARROW_RIGHT},
{ui::VKEY_END, ui::DomCode::END, ui::EF_NONE, ui::DomKey::END}},
// Shift+Ctrl+Right -> Shift+End
{ui::ET_KEY_PRESSED,
{ui::VKEY_RIGHT, ui::DomCode::ARROW_RIGHT,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_RIGHT},
{ui::VKEY_END, ui::DomCode::END, ui::EF_SHIFT_DOWN, ui::DomKey::END}},
});
}
TEST_F(EventRewriterTest, TestRewriteKeyEventSentByXSendEvent) {
// Remap Control to Alt.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember control;
InitModifierKeyPref(&control, prefs::kLanguageRemapControlKeyTo,
ui::chromeos::ModifierKey::kAltKey);
SetupKeyboard("Internal Keyboard");
// Send left control press.
{
ui::KeyEvent keyevent(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL,
ui::DomCode::CONTROL_LEFT, ui::EF_FINAL,
ui::DomKey::CONTROL, ui::EventTimeForNow());
TestEventRewriterContinuation continuation;
// Control should NOT be remapped to Alt if EF_FINAL is set.
rewriter()->RewriteEvent(keyevent,
continuation.weak_ptr_factory_.GetWeakPtr());
EXPECT_TRUE(continuation.rewritten_events.empty());
EXPECT_EQ(1u, continuation.passthrough_events.size());
EXPECT_TRUE(continuation.passthrough_events[0]->IsKeyEvent());
const auto* result = continuation.passthrough_events[0]->AsKeyEvent();
EXPECT_EQ(ui::VKEY_CONTROL, result->key_code());
}
}
TEST_F(EventRewriterTest, TestRewriteNonNativeEvent) {
// Remap Control to Alt.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
IntegerPrefMember control;
InitModifierKeyPref(&control, prefs::kLanguageRemapControlKeyTo,
ui::chromeos::ModifierKey::kAltKey);
SetupKeyboard("Internal Keyboard");
const int kTouchId = 2;
gfx::Point location(0, 0);
ui::TouchEvent press(
ui::ET_TOUCH_PRESSED, location, base::TimeTicks(),
ui::PointerDetails(ui::EventPointerType::kTouch, kTouchId));
press.set_flags(ui::EF_CONTROL_DOWN);
TestEventRewriterContinuation continuation;
rewriter()->RewriteEvent(press, continuation.weak_ptr_factory_.GetWeakPtr());
EXPECT_TRUE(continuation.passthrough_events.empty());
EXPECT_EQ(1u, continuation.rewritten_events.size());
// Control should be remapped to Alt.
EXPECT_EQ(ui::EF_ALT_DOWN, continuation.rewritten_events[0]->flags() &
(ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN));
}
// Keeps a buffer of handled events.
class EventBuffer : public ui::test::TestEventProcessor {
public:
EventBuffer() {}
~EventBuffer() override {}
void PopEvents(std::vector<std::unique_ptr<ui::Event>>* events) {
events->clear();
events->swap(events_);
}
private:
// ui::EventSink overrides:
ui::EventDispatchDetails OnEventFromSource(ui::Event* event) override {
events_.push_back(ui::Event::Clone(*event));
return ui::EventDispatchDetails();
}
std::vector<std::unique_ptr<ui::Event>> events_;
DISALLOW_COPY_AND_ASSIGN(EventBuffer);
};
// Trivial EventSource that does nothing but send events.
class TestEventSource : public ui::EventSource {
public:
explicit TestEventSource(ui::EventProcessor* processor)
: processor_(processor) {}
ui::EventSink* GetEventSink() override { return processor_; }
ui::EventDispatchDetails Send(ui::Event* event) {
return SendEventToSink(event);
}
private:
ui::EventProcessor* processor_;
};
// Tests of event rewriting that depend on the Ash window manager.
class EventRewriterAshTest : public ChromeAshTestBase {
public:
EventRewriterAshTest()
: source_(&buffer_),
fake_user_manager_(new FakeChromeUserManager),
user_manager_enabler_(base::WrapUnique(fake_user_manager_)) {}
~EventRewriterAshTest() override {}
ui::EventDispatchDetails Send(ui::Event* event) {
return source_.Send(event);
}
void SendKeyEvent(ui::EventType type,
ui::KeyboardCode key_code,
ui::DomCode code,
ui::DomKey key) {
SendKeyEvent(type, key_code, code, key, ui::EF_NONE);
}
void SendKeyEvent(ui::EventType type,
ui::KeyboardCode key_code,
ui::DomCode code,
ui::DomKey key,
int flags) {
ui::KeyEvent press(type, key_code, code, flags, key, ui::EventTimeForNow());
ui::EventDispatchDetails details = Send(&press);
CHECK(!details.dispatcher_destroyed);
}
void SendActivateStickyKeyPattern(ui::KeyboardCode key_code,
ui::DomCode code,
ui::DomKey key) {
SendKeyEvent(ui::ET_KEY_PRESSED, key_code, code, key);
SendKeyEvent(ui::ET_KEY_RELEASED, key_code, code, key);
}
protected:
sync_preferences::TestingPrefServiceSyncable* prefs() { return &prefs_; }
void InitModifierKeyPref(IntegerPrefMember* int_pref,
const std::string& pref_name,
ui::chromeos::ModifierKey modifierKey) {
int_pref->Init(pref_name, prefs());
int_pref->SetValue(static_cast<int>(modifierKey));
}
void PopEvents(std::vector<std::unique_ptr<ui::Event>>* events) {
buffer_.PopEvents(events);
}
void SetUp() override {
ChromeAshTestBase::SetUp();
sticky_keys_controller_ = Shell::Get()->sticky_keys_controller();
delegate_ = std::make_unique<EventRewriterDelegateImpl>(nullptr);
delegate_->set_pref_service_for_testing(prefs());
rewriter_ = std::make_unique<ui::EventRewriterChromeOS>(
delegate_.get(), sticky_keys_controller_, false, &fake_ime_keyboard_);
chromeos::Preferences::RegisterProfilePrefs(prefs_.registry());
source_.AddEventRewriter(rewriter_.get());
sticky_keys_controller_->Enable(true);
}
void TearDown() override {
rewriter_.reset();
ChromeAshTestBase::TearDown();
}
protected:
StickyKeysController* sticky_keys_controller_;
private:
std::unique_ptr<EventRewriterDelegateImpl> delegate_;
chromeos::input_method::FakeImeKeyboard fake_ime_keyboard_;
std::unique_ptr<ui::EventRewriterChromeOS> rewriter_;
EventBuffer buffer_;
TestEventSource source_;
FakeChromeUserManager* fake_user_manager_; // Not owned.
user_manager::ScopedUserManager user_manager_enabler_;
sync_preferences::TestingPrefServiceSyncable prefs_;
DISALLOW_COPY_AND_ASSIGN(EventRewriterAshTest);
};
TEST_F(EventRewriterAshTest, TopRowKeysAreFunctionKeys) {
std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(1));
wm::ActivateWindow(window.get());
std::vector<std::unique_ptr<ui::Event>> events;
// Create a simulated keypress of F1 targetted at the window.
ui::KeyEvent press_f1(ui::ET_KEY_PRESSED, ui::VKEY_F1, ui::DomCode::F1,
ui::EF_NONE, ui::DomKey::F1, ui::EventTimeForNow());
// The event should also not be rewritten if the send-function-keys pref is
// additionally set, for both apps v2 and regular windows.
BooleanPrefMember send_function_keys_pref;
send_function_keys_pref.Init(prefs::kLanguageSendFunctionKeys, prefs());
send_function_keys_pref.SetValue(true);
ui::EventDispatchDetails details = Send(&press_f1);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(1u, events.size());
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_F1,
ui::DomCode::F1, ui::EF_NONE,
ui::DomKey::F1, kNoScanCode),
GetKeyEventAsString(*static_cast<ui::KeyEvent*>(events[0].get())));
// If the pref isn't set when an event is sent to a regular window, F1 is
// rewritten to the back key.
send_function_keys_pref.SetValue(false);
details = Send(&press_f1);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(1u, events.size());
EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK,
ui::DomCode::BROWSER_BACK, ui::EF_NONE,
ui::DomKey::BROWSER_BACK, kNoScanCode),
GetKeyEventAsString(*static_cast<ui::KeyEvent*>(events[0].get())));
}
// Parameterized version of test with the same name that accepts the
// event flags that correspond to a right-click. This will be either
// Alt+Click or Search+Click. After a transition period this will
// default to Search+Click and the Alt+Click logic will be removed.
void EventRewriterTest::DontRewriteIfNotRewritten(int right_click_flags) {
ui::DeviceDataManager* device_data_manager =
ui::DeviceDataManager::GetInstance();
std::vector<ui::InputDevice> touchpad_devices(2);
constexpr int kTouchpadId1 = 10;
constexpr int kTouchpadId2 = 11;
touchpad_devices[0].id = kTouchpadId1;
touchpad_devices[1].id = kTouchpadId2;
static_cast<ui::DeviceHotplugEventObserver*>(device_data_manager)
->OnTouchpadDevicesUpdated(touchpad_devices);
std::vector<ui::InputDevice> mouse_devices(1);
constexpr int kMouseId = 12;
touchpad_devices[0].id = kMouseId;
static_cast<ui::DeviceHotplugEventObserver*>(device_data_manager)
->OnMouseDevicesUpdated(mouse_devices);
// Test (Alt|Search) + Left click.
{
ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), right_click_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_press(&press);
test_press.set_source_device_id(kTouchpadId1);
// Sanity check.
EXPECT_EQ(ui::ET_MOUSE_PRESSED, press.type());
EXPECT_EQ(right_click_flags, press.flags());
const ui::MouseEvent result = RewriteMouseButtonEvent(press);
EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result.flags());
EXPECT_NE(right_click_flags, right_click_flags & result.flags());
EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result.changed_button_flags());
}
{
ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), right_click_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_release(&release);
test_release.set_source_device_id(kTouchpadId1);
const ui::MouseEvent result = RewriteMouseButtonEvent(release);
EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result.flags());
EXPECT_NE(right_click_flags, right_click_flags & result.flags());
EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result.changed_button_flags());
}
// No (ALT|SEARCH) in first click.
{
ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_press(&press);
test_press.set_source_device_id(kTouchpadId1);
const ui::MouseEvent result = RewriteMouseButtonEvent(press);
EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & result.flags());
EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result.changed_button_flags());
}
{
ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), right_click_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_release(&release);
test_release.set_source_device_id(kTouchpadId1);
const ui::MouseEvent result = RewriteMouseButtonEvent(release);
EXPECT_EQ(right_click_flags, right_click_flags & result.flags());
EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result.changed_button_flags());
}
// ALT on different device.
{
ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), right_click_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_press(&press);
test_press.set_source_device_id(kTouchpadId2);
const ui::MouseEvent result = RewriteMouseButtonEvent(press);
EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result.flags());
EXPECT_NE(right_click_flags, right_click_flags & result.flags());
EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result.changed_button_flags());
}
{
ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), right_click_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_release(&release);
test_release.set_source_device_id(kTouchpadId1);
const ui::MouseEvent result = RewriteMouseButtonEvent(release);
EXPECT_EQ(right_click_flags, right_click_flags & result.flags());
EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result.changed_button_flags());
}
{
ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), right_click_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_release(&release);
test_release.set_source_device_id(kTouchpadId2);
const ui::MouseEvent result = RewriteMouseButtonEvent(release);
EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result.flags());
EXPECT_NE(right_click_flags, right_click_flags & result.flags());
EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result.changed_button_flags());
}
// No rewrite for non-touchpad devices.
{
ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), right_click_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_press(&press);
test_press.set_source_device_id(kMouseId);
EXPECT_EQ(ui::ET_MOUSE_PRESSED, press.type());
EXPECT_EQ(right_click_flags, press.flags());
const ui::MouseEvent result = RewriteMouseButtonEvent(press);
EXPECT_EQ(right_click_flags, right_click_flags & result.flags());
EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result.changed_button_flags());
}
{
ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), right_click_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_release(&release);
test_release.set_source_device_id(kMouseId);
const ui::MouseEvent result = RewriteMouseButtonEvent(release);
EXPECT_EQ(right_click_flags, right_click_flags & result.flags());
EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result.changed_button_flags());
}
}
TEST_F(EventRewriterTest, DontRewriteIfNotRewritten_AltClickIsRightClick) {
DontRewriteIfNotRewritten(ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN);
EXPECT_EQ(message_center_.NotificationCount(), 0);
}
TEST_F(EventRewriterTest, DontRewriteIfNotRewritten_AltClickIsRightClick_New) {
// Enabling the kImprovedKeyboardShortcuts feature does not change alt+click
// behavior or create a notification.
scoped_feature_list_.InitAndEnableFeature(
::features::kImprovedKeyboardShortcuts);
DontRewriteIfNotRewritten(ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN);
EXPECT_EQ(message_center_.NotificationCount(), 0);
}
TEST_F(EventRewriterTest, DontRewriteIfNotRewritten_SearchClickIsRightClick) {
scoped_feature_list_.InitAndEnableFeature(
features::kUseSearchClickForRightClick);
DontRewriteIfNotRewritten(ui::EF_LEFT_MOUSE_BUTTON | ui::EF_COMMAND_DOWN);
EXPECT_EQ(message_center_.NotificationCount(), 0);
}
TEST_F(EventRewriterTest, DontRewriteIfNotRewritten_AltClickDeprecated) {
// Pressing search+click with alt+click deprecated works, but does not
// generate a notification.
scoped_feature_list_.InitAndEnableFeature(::features::kDeprecateAltClick);
DontRewriteIfNotRewritten(ui::EF_LEFT_MOUSE_BUTTON | ui::EF_COMMAND_DOWN);
EXPECT_EQ(message_center_.NotificationCount(), 0);
}
TEST_F(EventRewriterTest, DeprecatedAltClickGeneratesNotification) {
scoped_feature_list_.InitAndEnableFeature(::features::kDeprecateAltClick);
ui::DeviceDataManager* device_data_manager =
ui::DeviceDataManager::GetInstance();
std::vector<ui::InputDevice> touchpad_devices(1);
constexpr int kTouchpadId1 = 10;
touchpad_devices[0].id = kTouchpadId1;
static_cast<ui::DeviceHotplugEventObserver*>(device_data_manager)
->OnTouchpadDevicesUpdated(touchpad_devices);
std::vector<ui::InputDevice> mouse_devices(1);
constexpr int kMouseId = 12;
touchpad_devices[0].id = kMouseId;
static_cast<ui::DeviceHotplugEventObserver*>(device_data_manager)
->OnMouseDevicesUpdated(mouse_devices);
const int deprecated_flags = ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN;
// Alt + Left click => No rewrite.
{
ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), deprecated_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_press(&press);
test_press.set_source_device_id(kTouchpadId1);
// Sanity check.
EXPECT_EQ(ui::ET_MOUSE_PRESSED, press.type());
EXPECT_EQ(deprecated_flags, press.flags());
const ui::MouseEvent result = RewriteMouseButtonEvent(press);
// No rewrite occurred.
EXPECT_EQ(deprecated_flags, deprecated_flags & result.flags());
EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result.changed_button_flags());
// Expect a deprecation notification.
EXPECT_EQ(message_center_.NotificationCount(), 1);
ClearNotifications();
}
{
ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), deprecated_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_release(&release);
test_release.set_source_device_id(kTouchpadId1);
const ui::MouseEvent result = RewriteMouseButtonEvent(release);
// No rewrite occurred.
EXPECT_EQ(deprecated_flags, deprecated_flags & result.flags());
EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result.changed_button_flags());
// Don't expect a new notification on release.
EXPECT_EQ(message_center_.NotificationCount(), 0);
}
// No rewrite or notification for non-touchpad devices.
{
ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), deprecated_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_press(&press);
test_press.set_source_device_id(kMouseId);
EXPECT_EQ(ui::ET_MOUSE_PRESSED, press.type());
EXPECT_EQ(deprecated_flags, press.flags());
const ui::MouseEvent result = RewriteMouseButtonEvent(press);
EXPECT_EQ(deprecated_flags, deprecated_flags & result.flags());
EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result.changed_button_flags());
// No notification expected for this case.
EXPECT_EQ(message_center_.NotificationCount(), 0);
}
{
ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), deprecated_flags,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventTestApi test_release(&release);
test_release.set_source_device_id(kMouseId);
const ui::MouseEvent result = RewriteMouseButtonEvent(release);
EXPECT_EQ(deprecated_flags, deprecated_flags & result.flags());
EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result.changed_button_flags());
// No notification expected for this case.
EXPECT_EQ(message_center_.NotificationCount(), 0);
}
}
TEST_F(EventRewriterAshTest, StickyKeyEventDispatchImpl) {
// Test the actual key event dispatch implementation.
std::vector<std::unique_ptr<ui::Event>> events;
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
PopEvents(&events);
EXPECT_EQ(1u, events.size());
EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type());
EXPECT_EQ(ui::VKEY_CONTROL,
static_cast<ui::KeyEvent*>(events[0].get())->key_code());
// Test key press event is correctly modified and modifier release
// event is sent.
ui::KeyEvent press(ui::ET_KEY_PRESSED, ui::VKEY_C, ui::DomCode::US_C,
ui::EF_NONE, ui::DomKey::Constant<'c'>::Character,
ui::EventTimeForNow());
ui::EventDispatchDetails details = Send(&press);
PopEvents(&events);
EXPECT_EQ(2u, events.size());
EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type());
EXPECT_EQ(ui::VKEY_C,
static_cast<ui::KeyEvent*>(events[0].get())->key_code());
EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
EXPECT_EQ(ui::VKEY_CONTROL,
static_cast<ui::KeyEvent*>(events[1].get())->key_code());
// Test key release event is not modified.
ui::KeyEvent release(ui::ET_KEY_RELEASED, ui::VKEY_C, ui::DomCode::US_C,
ui::EF_NONE, ui::DomKey::Constant<'c'>::Character,
ui::EventTimeForNow());
details = Send(&release);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(1u, events.size());
EXPECT_EQ(ui::ET_KEY_RELEASED, events[0]->type());
EXPECT_EQ(ui::VKEY_C,
static_cast<ui::KeyEvent*>(events[0].get())->key_code());
EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN);
}
TEST_F(EventRewriterAshTest, MouseEventDispatchImpl) {
std::vector<std::unique_ptr<ui::Event>> events;
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
PopEvents(&events);
// Test mouse press event is correctly modified.
gfx::Point location(0, 0);
ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventDispatchDetails details = Send(&press);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(1u, events.size());
EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0]->type());
EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
// Test mouse release event is correctly modified and modifier release
// event is sent. The mouse event should have the correct DIP location.
ui::MouseEvent release(ui::ET_MOUSE_RELEASED, location, location,
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
details = Send(&release);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(2u, events.size());
EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type());
EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
EXPECT_EQ(ui::VKEY_CONTROL,
static_cast<ui::KeyEvent*>(events[1].get())->key_code());
}
TEST_F(EventRewriterAshTest, MouseWheelEventDispatchImpl) {
std::vector<std::unique_ptr<ui::Event>> events;
// Test positive mouse wheel event is correctly modified and modifier release
// event is sent.
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
PopEvents(&events);
gfx::Point location(0, 0);
ui::MouseWheelEvent positive(
gfx::Vector2d(0, ui::MouseWheelEvent::kWheelDelta), location, location,
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventDispatchDetails details = Send(&positive);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(2u, events.size());
EXPECT_TRUE(events[0]->IsMouseWheelEvent());
EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
EXPECT_EQ(ui::VKEY_CONTROL,
static_cast<ui::KeyEvent*>(events[1].get())->key_code());
// Test negative mouse wheel event is correctly modified and modifier release
// event is sent.
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
PopEvents(&events);
ui::MouseWheelEvent negative(
gfx::Vector2d(0, -ui::MouseWheelEvent::kWheelDelta), location, location,
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
details = Send(&negative);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(2u, events.size());
EXPECT_TRUE(events[0]->IsMouseWheelEvent());
EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
EXPECT_EQ(ui::VKEY_CONTROL,
static_cast<ui::KeyEvent*>(events[1].get())->key_code());
}
// Tests that if modifier keys are remapped, the flags of a mouse wheel event
// will be rewritten properly.
TEST_F(EventRewriterAshTest, MouseWheelEventModifiersRewritten) {
// Generate a mouse wheel event that has a CONTROL_DOWN modifier flag and
// expect that no rewriting happens as no modifier remapping is active.
std::vector<std::unique_ptr<ui::Event>> events;
gfx::Point location(0, 0);
ui::MouseWheelEvent positive(
gfx::Vector2d(0, ui::MouseWheelEvent::kWheelDelta), location, location,
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON | ui::EF_CONTROL_DOWN,
ui::EF_LEFT_MOUSE_BUTTON);
ui::EventDispatchDetails details = Send(&positive);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(1u, events.size());
EXPECT_TRUE(events[0]->IsMouseWheelEvent());
EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
// Remap Control to Alt.
IntegerPrefMember control;
InitModifierKeyPref(&control, prefs::kLanguageRemapControlKeyTo,
ui::chromeos::ModifierKey::kAltKey);
// Sends the same events once again and expect that it will be rewritten to
// ALT_DOWN.
details = Send(&positive);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(1u, events.size());
EXPECT_TRUE(events[0]->IsMouseWheelEvent());
EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN);
EXPECT_TRUE(events[0]->flags() & ui::EF_ALT_DOWN);
}
// Tests edge cases of key event rewriting (see https://crbug.com/913209).
TEST_F(EventRewriterAshTest, KeyEventRewritingEdgeCases) {
std::vector<std::unique_ptr<ui::Event>> events;
// Edge case 1: Press the Launcher button first. Then press the Up Arrow
// button.
SendKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_COMMAND, ui::DomCode::META_LEFT,
ui::DomKey::META);
SendKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::DomKey::ARROW_UP, ui::EF_COMMAND_DOWN);
PopEvents(&events);
EXPECT_EQ(2u, events.size());
events.clear();
SendKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_COMMAND, ui::DomCode::META_LEFT,
ui::DomKey::META);
PopEvents(&events);
// When releasing the Launcher button, the rewritten event should be released
// as well.
EXPECT_EQ(2u, events.size());
EXPECT_EQ(ui::VKEY_COMMAND,
static_cast<ui::KeyEvent*>(events[0].get())->key_code());
EXPECT_EQ(ui::VKEY_PRIOR,
static_cast<ui::KeyEvent*>(events[1].get())->key_code());
events.clear();
// Edge case 2: Press the Up Arrow button first. Then press the Launch button.
SendKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::DomKey::ARROW_UP);
SendKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_COMMAND, ui::DomCode::META_LEFT,
ui::DomKey::META);
PopEvents(&events);
EXPECT_EQ(2u, events.size());
events.clear();
SendKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_UP, ui::DomCode::ARROW_UP,
ui::DomKey::ARROW_UP, ui::EF_COMMAND_DOWN);
PopEvents(&events);
// When releasing the Up Arrow button, the rewritten event should be blocked.
EXPECT_EQ(1u, events.size());
EXPECT_EQ(ui::VKEY_UP,
static_cast<ui::KeyEvent*>(events[0].get())->key_code());
}
TEST_F(EventRewriterAshTest, ScrollEventDispatchImpl) {
std::vector<std::unique_ptr<ui::Event>> events;
// Test scroll event is correctly modified.
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
PopEvents(&events);
gfx::PointF location(0, 0);
ui::ScrollEvent scroll(ui::ET_SCROLL, location, location,
ui::EventTimeForNow(), 0 /* flag */, 0 /* x_offset */,
1 /* y_offset */, 0 /* x_offset_ordinal */,
1 /* y_offset_ordinal */, 2 /* finger */);
ui::EventDispatchDetails details = Send(&scroll);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(1u, events.size());
EXPECT_TRUE(events[0]->IsScrollEvent());
EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
// Test FLING_START event deactivates the sticky key, but is modified.
ui::ScrollEvent fling_start(
ui::ET_SCROLL_FLING_START, location, location, ui::EventTimeForNow(),
0 /* flag */, 0 /* x_offset */, 0 /* y_offset */,
0 /* x_offset_ordinal */, 0 /* y_offset_ordinal */, 2 /* finger */);
details = Send(&fling_start);
PopEvents(&events);
EXPECT_EQ(2u, events.size());
EXPECT_TRUE(events[0]->IsScrollEvent());
EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
EXPECT_EQ(ui::VKEY_CONTROL,
static_cast<ui::KeyEvent*>(events[1].get())->key_code());
// Test scroll direction change causes that modifier release event is sent.
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
details = Send(&scroll);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
ui::ScrollEvent scroll2(ui::ET_SCROLL, location, location,
ui::EventTimeForNow(), 0 /* flag */, 0 /* x_offset */,
-1 /* y_offset */, 0 /* x_offset_ordinal */,
-1 /* y_offset_ordinal */, 2 /* finger */);
details = Send(&scroll2);
ASSERT_FALSE(details.dispatcher_destroyed);
PopEvents(&events);
EXPECT_EQ(2u, events.size());
EXPECT_TRUE(events[0]->IsScrollEvent());
EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN);
EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
EXPECT_EQ(ui::VKEY_CONTROL,
static_cast<ui::KeyEvent*>(events[1].get())->key_code());
}
class StickyKeysOverlayTest : public EventRewriterAshTest {
public:
StickyKeysOverlayTest() : overlay_(NULL) {}
~StickyKeysOverlayTest() override {}
void SetUp() override {
EventRewriterAshTest::SetUp();
overlay_ = sticky_keys_controller_->GetOverlayForTest();
ASSERT_TRUE(overlay_);
}
StickyKeysOverlay* overlay_;
};
TEST_F(StickyKeysOverlayTest, OneModifierEnabled) {
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
// Pressing modifier key should show overlay.
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
// Pressing a normal key should hide overlay.
SendActivateStickyKeyPattern(ui::VKEY_T, ui::DomCode::US_T,
ui::DomKey::Constant<'t'>::Character);
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
}
TEST_F(StickyKeysOverlayTest, TwoModifiersEnabled) {
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
// Pressing two modifiers should show overlay.
SendActivateStickyKeyPattern(ui::VKEY_SHIFT, ui::DomCode::SHIFT_LEFT,
ui::DomKey::SHIFT);
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
// Pressing a normal key should hide overlay.
SendActivateStickyKeyPattern(ui::VKEY_N, ui::DomCode::US_N,
ui::DomKey::Constant<'n'>::Character);
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
}
TEST_F(StickyKeysOverlayTest, LockedModifier) {
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
// Pressing a modifier key twice should lock modifier and show overlay.
SendActivateStickyKeyPattern(ui::VKEY_LMENU, ui::DomCode::ALT_LEFT,
ui::DomKey::ALT);
SendActivateStickyKeyPattern(ui::VKEY_LMENU, ui::DomCode::ALT_LEFT,
ui::DomKey::ALT);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
// Pressing a normal key should not hide overlay.
SendActivateStickyKeyPattern(ui::VKEY_D, ui::DomCode::US_D,
ui::DomKey::Constant<'d'>::Character);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
}
TEST_F(StickyKeysOverlayTest, LockedAndNormalModifier) {
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
// Pressing a modifier key twice should lock modifier and show overlay.
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
// Pressing another modifier key should still show overlay.
SendActivateStickyKeyPattern(ui::VKEY_SHIFT, ui::DomCode::SHIFT_LEFT,
ui::DomKey::SHIFT);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
// Pressing a normal key should not hide overlay but disable normal modifier.
SendActivateStickyKeyPattern(ui::VKEY_D, ui::DomCode::US_D,
ui::DomKey::Constant<'d'>::Character);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
}
TEST_F(StickyKeysOverlayTest, ModifiersDisabled) {
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_COMMAND_DOWN));
// Enable modifiers.
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
SendActivateStickyKeyPattern(ui::VKEY_SHIFT, ui::DomCode::SHIFT_LEFT,
ui::DomKey::SHIFT);
SendActivateStickyKeyPattern(ui::VKEY_SHIFT, ui::DomCode::SHIFT_LEFT,
ui::DomKey::SHIFT);
SendActivateStickyKeyPattern(ui::VKEY_LMENU, ui::DomCode::ALT_LEFT,
ui::DomKey::ALT);
SendActivateStickyKeyPattern(ui::VKEY_COMMAND, ui::DomCode::META_LEFT,
ui::DomKey::META);
SendActivateStickyKeyPattern(ui::VKEY_COMMAND, ui::DomCode::META_LEFT,
ui::DomKey::META);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_COMMAND_DOWN));
// Disable modifiers and overlay should be hidden.
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
ui::DomKey::CONTROL);
SendActivateStickyKeyPattern(ui::VKEY_SHIFT, ui::DomCode::SHIFT_LEFT,
ui::DomKey::SHIFT);
SendActivateStickyKeyPattern(ui::VKEY_LMENU, ui::DomCode::ALT_LEFT,
ui::DomKey::ALT);
SendActivateStickyKeyPattern(ui::VKEY_LMENU, ui::DomCode::ALT_LEFT,
ui::DomKey::ALT);
SendActivateStickyKeyPattern(ui::VKEY_COMMAND, ui::DomCode::META_LEFT,
ui::DomKey::META);
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_COMMAND_DOWN));
}
TEST_F(StickyKeysOverlayTest, ModifierVisibility) {
// All but AltGr and Mod3 should initially be visible.
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_CONTROL_DOWN));
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_SHIFT_DOWN));
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALT_DOWN));
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_COMMAND_DOWN));
EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN));
EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN));
// Turn all modifiers on.
sticky_keys_controller_->SetModifiersEnabled(true, true);
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_CONTROL_DOWN));
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_SHIFT_DOWN));
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALT_DOWN));
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_COMMAND_DOWN));
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN));
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN));
// Turn off Mod3.
sticky_keys_controller_->SetModifiersEnabled(false, true);
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN));
EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN));
// Turn off AltGr.
sticky_keys_controller_->SetModifiersEnabled(true, false);
EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN));
EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN));
// Turn off AltGr and Mod3.
sticky_keys_controller_->SetModifiersEnabled(false, false);
EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN));
EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN));
}
class ExtensionRewriterInputTest : public EventRewriterAshTest,
public ui::EventRewriterChromeOS::Delegate {
public:
ExtensionRewriterInputTest() = default;
ExtensionRewriterInputTest(const ExtensionRewriterInputTest&) = delete;
ExtensionRewriterInputTest& operator=(const ExtensionRewriterInputTest&) =
delete;
~ExtensionRewriterInputTest() override {}
void SetUp() override {
EventRewriterAshTest::SetUp();
event_rewriter_chromeos_ = std::make_unique<ui::EventRewriterChromeOS>(
this, nullptr, false, &fake_ime_keyboard_);
}
void SetModifierRemapping(const std::string& pref_name,
ui::chromeos::ModifierKey value) {
modifier_remapping_[pref_name] = static_cast<int>(value);
}
void RegisterExtensionShortcut(ui::KeyboardCode key_code, int flags) {
constexpr int kModifierMasks = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN;
// No other masks should be present aside from the ones speicifed in
// kModifierMasks.
DCHECK((flags & kModifierMasks) == flags);
registered_extension_shortcuts_.emplace(key_code, flags);
}
void RemoveAllExtensionShortcuts() {
registered_extension_shortcuts_.clear();
}
void ExpectEventRewrittenTo(const KeyTestCase& test) {
CheckKeyTestCase(event_rewriter_chromeos_.get(), test);
}
protected:
sync_preferences::TestingPrefServiceSyncable prefs_;
chromeos::input_method::FakeImeKeyboard fake_ime_keyboard_;
std::unique_ptr<ui::EventRewriterChromeOS> event_rewriter_chromeos_;
private:
// ui::EventRewriterChromeOS::Delegate:
bool RewriteModifierKeys() override { return true; }
bool GetKeyboardRemappedPrefValue(const std::string& pref_name,
int* value) const override {
auto it = modifier_remapping_.find(pref_name);
if (it == modifier_remapping_.end()) {
return false;
}
*value = it->second;
return true;
}
bool TopRowKeysAreFunctionKeys() const override { return false; }
bool IsExtensionCommandRegistered(ui::KeyboardCode key_code,
int flags) const override {
return base::Contains(registered_extension_shortcuts_,
ui::Accelerator(key_code, flags));
}
bool IsSearchKeyAcceleratorReserved() const override { return false; }
bool NotifyDeprecatedRightClickRewrite() override { return false; }
bool NotifyDeprecatedFKeyRewrite() override { return false; }
bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code) override {
return false;
}
std::map<std::string, int> modifier_remapping_;
base::flat_set<ui::Accelerator> registered_extension_shortcuts_;
};
TEST_F(ExtensionRewriterInputTest, RewrittenModifier) {
// Register Control + B as an extension shortcut.
RegisterExtensionShortcut(ui::VKEY_B, ui::EF_CONTROL_DOWN);
// Check that standard extension input has no rewritten modifiers.
ExpectEventRewrittenTo({ui::ET_KEY_PRESSED,
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'b'>::Character},
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'b'>::Character}});
// Remap Control -> Alt.
SetModifierRemapping(prefs::kLanguageRemapControlKeyTo,
ui::chromeos::ModifierKey::kAltKey);
// Pressing Control + B should now be remapped to Alt + B.
ExpectEventRewrittenTo({ui::ET_KEY_PRESSED,
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'b'>::Character},
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'b'>::Character}});
// Remap Alt -> Control.
SetModifierRemapping(prefs::kLanguageRemapAltKeyTo,
ui::chromeos::ModifierKey::kControlKey);
// Pressing Alt + B should now be remapped to Control + B.
ExpectEventRewrittenTo({ui::ET_KEY_PRESSED,
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'b'>::Character},
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'b'>::Character}});
// Remove all extension shortcuts and still expect the remapping to work.
RemoveAllExtensionShortcuts();
ExpectEventRewrittenTo({ui::ET_KEY_PRESSED,
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'b'>::Character},
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'b'>::Character}});
ExpectEventRewrittenTo({ui::ET_KEY_PRESSED,
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_ALT_DOWN,
ui::DomKey::Constant<'b'>::Character},
{ui::VKEY_B, ui::DomCode::US_B, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'b'>::Character}});
}
TEST_F(ExtensionRewriterInputTest, RewriteNumpadExtensionCommand) {
// Register Control + NUMPAD1 as an extension shortcut.
RegisterExtensionShortcut(ui::VKEY_NUMPAD1, ui::EF_CONTROL_DOWN);
// Check that extension shortcuts that involve numpads keys are properly
// rewritten. Note that VKEY_END is associated with NUMPAD1 if Num Lock is
// disabled. The result should be "NumPad 1 with Control".
ExpectEventRewrittenTo(
{ui::ET_KEY_PRESSED,
{ui::VKEY_END, ui::DomCode::NUMPAD1, ui::EF_CONTROL_DOWN,
ui::DomKey::END},
{ui::VKEY_NUMPAD1, ui::DomCode::NUMPAD1, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'1'>::Character}});
// Remove the extension shortcut and expect the numpad event to still be
// rewritten.
RemoveAllExtensionShortcuts();
ExpectEventRewrittenTo(
{ui::ET_KEY_PRESSED,
{ui::VKEY_END, ui::DomCode::NUMPAD1, ui::EF_CONTROL_DOWN,
ui::DomKey::END},
{ui::VKEY_NUMPAD1, ui::DomCode::NUMPAD1, ui::EF_CONTROL_DOWN,
ui::DomKey::Constant<'1'>::Character}});
}
} // namespace ash