// 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 "chrome/browser/chromeos/events/event_rewriter.h" #include #include "ash/shell.h" #include "ash/sticky_keys/sticky_keys_controller.h" #include "ash/sticky_keys/sticky_keys_overlay.h" #include "ash/test/ash_test_base.h" #include "ash/wm/window_state.h" #include "base/basictypes.h" #include "base/command_line.h" #include "base/prefs/pref_member.h" #include "base/strings/stringprintf.h" #include "chrome/browser/chromeos/input_method/input_method_configuration.h" #include "chrome/browser/chromeos/input_method/mock_input_method_manager.h" #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" #include "chrome/browser/chromeos/preferences.h" #include "chrome/common/pref_names.h" #include "chromeos/chromeos_switches.h" #include "components/syncable_prefs/testing_pref_service_syncable.h" #include "components/user_manager/fake_user_manager.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/ime/chromeos/fake_ime_keyboard.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" #if defined(USE_X11) #include #include "ui/events/devices/x11/touch_factory_x11.h" #include "ui/events/event_utils.h" #include "ui/events/test/events_test_utils_x11.h" #include "ui/gfx/x/x11_types.h" #endif namespace { // The device id of the test touchpad device. #if defined(USE_X11) const int kTouchPadDeviceId = 1; #endif const int kKeyboardDeviceId = 2; std::string GetExpectedResultAsString(ui::EventType ui_type, ui::KeyboardCode ui_keycode, ui::DomCode code, int ui_flags, // ui::EventFlags ui::DomKey key) { return base::StringPrintf( "type=%d code=0x%06X flags=0x%X vk=0x%02X key=0x%08X", ui_type, static_cast(code), ui_flags & ~ui::EF_IS_REPEAT, ui_keycode, static_cast(key)); } std::string GetKeyEventAsString(const ui::KeyEvent& keyevent) { return GetExpectedResultAsString(keyevent.type(), keyevent.key_code(), keyevent.code(), keyevent.flags(), keyevent.GetDomKey()); } std::string GetRewrittenEventAsString(chromeos::EventRewriter* rewriter, ui::EventType ui_type, ui::KeyboardCode ui_keycode, ui::DomCode code, int ui_flags, // ui::EventFlags ui::DomKey key) { const ui::KeyEvent event(ui_type, ui_keycode, code, ui_flags, key, ui::EventTimeForNow()); scoped_ptr new_event; rewriter->RewriteEvent(event, &new_event); if (new_event) return GetKeyEventAsString( static_cast(*new_event.get())); 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; } input, expected; }; std::string GetTestCaseAsString(ui::EventType ui_type, const KeyTestCase::Event& test) { return GetExpectedResultAsString(ui_type, test.key_code, test.code, test.flags, test.key); } // Tests a single stateless key rewrite operation. void CheckKeyTestCase(chromeos::EventRewriter* rewriter, const KeyTestCase& test) { SCOPED_TRACE("\nSource: " + GetTestCaseAsString(test.type, test.input)); std::string expected = GetTestCaseAsString(test.type, test.expected); EXPECT_EQ(expected, GetRewrittenEventAsString( rewriter, test.type, test.input.key_code, test.input.code, test.input.flags, test.input.key)); } } // namespace namespace chromeos { class EventRewriterTest : public ash::test::AshTestBase { public: EventRewriterTest() : fake_user_manager_(new user_manager::FakeUserManager), user_manager_enabler_(fake_user_manager_), input_method_manager_mock_(NULL) {} ~EventRewriterTest() override {} void SetUp() override { input_method_manager_mock_ = new chromeos::input_method::MockInputMethodManager; chromeos::input_method::InitializeForTesting( input_method_manager_mock_); // pass ownership AshTestBase::SetUp(); } void TearDown() override { AshTestBase::TearDown(); // Shutdown() deletes the IME mock object. chromeos::input_method::Shutdown(); } protected: void TestRewriteNumPadKeys(); void TestRewriteNumPadKeysOnAppleKeyboard(); const ui::MouseEvent* RewriteMouseButtonEvent( chromeos::EventRewriter* rewriter, const ui::MouseEvent& event, scoped_ptr* new_event) { rewriter->RewriteMouseButtonEventForTesting(event, new_event); return *new_event ? static_cast(new_event->get()) : &event; } user_manager::FakeUserManager* fake_user_manager_; // Not owned. chromeos::ScopedUserManagerEnabler user_manager_enabler_; chromeos::input_method::MockInputMethodManager* input_method_manager_mock_; }; TEST_F(EventRewriterTest, TestRewriteCommandToControl) { // First, test with a PC keyboard. syncable_prefs::TestingPrefServiceSyncable prefs; EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_last_keyboard_device_id_for_testing(kKeyboardDeviceId); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase pc_keyboard_tests[] = { // VKEY_A, Alt modifier. {ui::ET_KEY_PRESSED, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_ALT_DOWN, ui::DomKey::UNIDENTIFIED}, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_ALT_DOWN, ui::DomKey::UNIDENTIFIED}}, // VKEY_A, Win modifier. {ui::ET_KEY_PRESSED, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_COMMAND_DOWN, ui::DomKey::UNIDENTIFIED}, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_COMMAND_DOWN, ui::DomKey::UNIDENTIFIED}}, // VKEY_A, Alt+Win modifier. {ui::ET_KEY_PRESSED, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::UNIDENTIFIED}, {ui::VKEY_A, ui::DomCode::KEY_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::OS_LEFT, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}}, // VKEY_RWIN (right Windows key), Alt modifier. {ui::ET_KEY_PRESSED, {ui::VKEY_RWIN, ui::DomCode::OS_RIGHT, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {ui::VKEY_RWIN, ui::DomCode::OS_RIGHT, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}}, }; for (const auto& test : pc_keyboard_tests) { CheckKeyTestCase(&rewriter, test); } // An Apple keyboard reusing the ID, zero. rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "Apple Keyboard"); rewriter.set_last_keyboard_device_id_for_testing(kKeyboardDeviceId); KeyTestCase apple_keyboard_tests[] = { // VKEY_A, Alt modifier. {ui::ET_KEY_PRESSED, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_ALT_DOWN, ui::DomKey::UNIDENTIFIED}, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_ALT_DOWN, ui::DomKey::UNIDENTIFIED}}, // VKEY_A, Win modifier. {ui::ET_KEY_PRESSED, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_COMMAND_DOWN, ui::DomKey::UNIDENTIFIED}, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_CONTROL_DOWN, ui::DomKey::Constant<'a'>::Character}}, // VKEY_A, Alt+Win modifier. {ui::ET_KEY_PRESSED, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::UNIDENTIFIED}, {ui::VKEY_A, ui::DomCode::KEY_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::OS_LEFT, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::OS}, {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::OS_RIGHT, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::OS}, {ui::VKEY_CONTROL, ui::DomCode::CONTROL_RIGHT, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::CONTROL}}, }; for (const auto& test : apple_keyboard_tests) { CheckKeyTestCase(&rewriter, test); } } // For crbug.com/133896. TEST_F(EventRewriterTest, TestRewriteCommandToControlWithControlRemapped) { // Remap Control to Alt. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember control; control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_last_keyboard_device_id_for_testing(kKeyboardDeviceId); KeyTestCase pc_keyboard_tests[] = { // 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}}, }; for (const auto& test : pc_keyboard_tests) { CheckKeyTestCase(&rewriter, test); } // An Apple keyboard reusing the ID, zero. rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "Apple Keyboard"); rewriter.set_last_keyboard_device_id_for_testing(kKeyboardDeviceId); KeyTestCase apple_keyboard_tests[] = { // 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::OS_LEFT, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::OS}, {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::OS_RIGHT, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::OS}, {ui::VKEY_CONTROL, ui::DomCode::CONTROL_RIGHT, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::CONTROL}}, }; for (const auto& test : apple_keyboard_tests) { CheckKeyTestCase(&rewriter, test); } } void EventRewriterTest::TestRewriteNumPadKeys() { syncable_prefs::TestingPrefServiceSyncable prefs; EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_last_keyboard_device_id_for_testing(kKeyboardDeviceId); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { // 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}}, }; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteNumPadKeys) { TestRewriteNumPadKeys(); } TEST_F(EventRewriterTest, TestRewriteNumPadKeysWithDiamondKeyFlag) { // Make sure the num lock works correctly even when Diamond key exists. const base::CommandLine original_cl(*base::CommandLine::ForCurrentProcess()); base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( chromeos::switches::kHasChromeOSDiamondKey, ""); TestRewriteNumPadKeys(); *base::CommandLine::ForCurrentProcess() = original_cl; } // Tests if the rewriter can handle a Command + Num Pad event. void EventRewriterTest::TestRewriteNumPadKeysOnAppleKeyboard() { syncable_prefs::TestingPrefServiceSyncable prefs; EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "Apple Keyboard"); rewriter.set_last_keyboard_device_id_for_testing(kKeyboardDeviceId); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { // 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}}}; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteNumPadKeysOnAppleKeyboard) { TestRewriteNumPadKeysOnAppleKeyboard(); } TEST_F(EventRewriterTest, TestRewriteNumPadKeysOnAppleKeyboardWithDiamondKeyFlag) { // Makes sure the num lock works correctly even when Diamond key exists. const base::CommandLine original_cl(*base::CommandLine::ForCurrentProcess()); base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( chromeos::switches::kHasChromeOSDiamondKey, ""); TestRewriteNumPadKeysOnAppleKeyboard(); *base::CommandLine::ForCurrentProcess() = original_cl; } TEST_F(EventRewriterTest, TestRewriteModifiersNoRemap) { syncable_prefs::TestingPrefServiceSyncable prefs; EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { // Press Search. Confirm the event is not rewritten. {ui::ET_KEY_PRESSED, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_NONE, ui::DomKey::OS}, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_COMMAND_DOWN, ui::DomKey::OS}}, // 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::OS_LEFT, ui::EF_NONE, ui::DomKey::OS}, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_NONE, ui::DomKey::OS}}, }; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteModifiersNoRemapMultipleKeys) { syncable_prefs::TestingPrefServiceSyncable prefs; EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { // 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 Search with Caps Lock mask. Confirm the event is not rewritten. {ui::ET_KEY_PRESSED, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_CAPS_LOCK_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_CAPS_LOCK_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}}, // Release Search with Caps Lock mask. Confirm the event is not rewritten. {ui::ET_KEY_RELEASED, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_CAPS_LOCK_DOWN, ui::DomKey::OS}, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_CAPS_LOCK_DOWN, ui::DomKey::OS}}, // Press Shift+Ctrl+Alt+Search+A. Confirm the event is not rewritten. {ui::ET_KEY_PRESSED, {ui::VKEY_B, ui::DomCode::KEY_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::KEY_B, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::Constant<'B'>::Character}}, }; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteModifiersDisableSome) { // Disable Search and Control keys. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember search; search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kVoidKey); IntegerPrefMember control; control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kVoidKey); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase disabled_modifier_tests[] = { // 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::OS_LEFT, ui::EF_NONE, ui::DomKey::OS}, {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 Control+Search. Confirm the event is now VKEY_UNKNOWN // without any modifiers. {ui::ET_KEY_PRESSED, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_CONTROL_DOWN, ui::DomKey::OS}, {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::KEY_A, ui::EF_CONTROL_DOWN, ui::DomKey::Constant<'a'>::Character}, {ui::VKEY_A, ui::DomCode::KEY_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::KEY_A, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ui::DomKey::Constant<'a'>::Character}, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_ALT_DOWN, ui::DomKey::Constant<'a'>::Character}}, }; for (const auto& test : disabled_modifier_tests) { CheckKeyTestCase(&rewriter, test); } // Remap Alt to Control. IntegerPrefMember alt; alt.Init(prefs::kLanguageRemapAltKeyTo, &prefs); alt.SetValue(chromeos::input_method::kControlKey); KeyTestCase tests[] = { // 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::KEY_A, ui::EF_ALT_DOWN, ui::DomKey::Constant<'a'>::Character}, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_CONTROL_DOWN, ui::DomKey::Constant<'a'>::Character}}, }; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteModifiersRemapToControl) { // Remap Search to Control. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember search; search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kControlKey); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase s_tests[] = { // Press Search. Confirm the event is now VKEY_CONTROL. {ui::ET_KEY_PRESSED, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN, ui::DomKey::CONTROL}}, }; for (const auto& test : s_tests) { CheckKeyTestCase(&rewriter, test); } // Remap Alt to Control too. IntegerPrefMember alt; alt.Init(prefs::kLanguageRemapAltKeyTo, &prefs); alt.SetValue(chromeos::input_method::kControlKey); KeyTestCase sa_tests[] = { // 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::OS_LEFT, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {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::OS_LEFT, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {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::OS_LEFT, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {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::KEY_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::KEY_B, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::Constant<'B'>::Character}}, }; for (const auto& test : sa_tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteModifiersRemapToEscape) { // Remap Search to ESC. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember search; search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kEscapeKey); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { // Press Search. Confirm the event is now VKEY_ESCAPE. {ui::ET_KEY_PRESSED, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {ui::VKEY_ESCAPE, ui::DomCode::ESCAPE, ui::EF_NONE, ui::DomKey::ESCAPE}}, }; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteModifiersRemapMany) { // Remap Search to Alt. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember search; search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kAltKey); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase s2a_tests[] = { // Press Search. Confirm the event is now VKEY_MENU. {ui::ET_KEY_PRESSED, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN, ui::DomKey::ALT}}, }; for (const auto& test : s2a_tests) { CheckKeyTestCase(&rewriter, test); } // Remap Alt to Control. IntegerPrefMember alt; alt.Init(prefs::kLanguageRemapAltKeyTo, &prefs); alt.SetValue(chromeos::input_method::kControlKey); KeyTestCase a2c_tests[] = { // 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}}, }; for (const auto& test : a2c_tests) { CheckKeyTestCase(&rewriter, test); } // Remap Control to Search. IntegerPrefMember control; control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kSearchKey); KeyTestCase c2s_tests[] = { // 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::OS_LEFT, ui::EF_COMMAND_DOWN, ui::DomKey::OS}}, // Then, press all of the three, Control+Alt+Search. {ui::ET_KEY_PRESSED, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {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+Search. {ui::ET_KEY_PRESSED, {ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::OS}, {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+Search+B {ui::ET_KEY_PRESSED, {ui::VKEY_B, ui::DomCode::KEY_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::KEY_B, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::Constant<'B'>::Character}}, }; for (const auto& test : c2s_tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteModifiersRemapToCapsLock) { // Remap Search to Caps Lock. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember search; search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kCapsLockKey); chromeos::input_method::FakeImeKeyboard ime_keyboard; EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); EXPECT_FALSE(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_DOWN, ui::DomKey::CAPS_LOCK), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_COMMAND_DOWN, ui::DomKey::OS)); // Confirm that the Caps Lock status is changed. EXPECT_TRUE(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), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_RELEASED, ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_NONE, ui::DomKey::OS)); // Confirm that the Caps Lock status is not changed. EXPECT_TRUE(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_DOWN | ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK), GetRewrittenEventAsString( &rewriter, ui::ET_KEY_PRESSED, ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_COMMAND_DOWN | ui::EF_CAPS_LOCK_DOWN, ui::DomKey::OS)); // Confirm that the Caps Lock status is changed. EXPECT_FALSE(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), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_RELEASED, ui::VKEY_LWIN, ui::DomCode::OS_LEFT, ui::EF_NONE, ui::DomKey::OS)); // Confirm that the Caps Lock status is not changed. EXPECT_FALSE(ime_keyboard.caps_lock_is_enabled_); // Press Caps Lock (on an external keyboard). EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK, ui::EF_CAPS_LOCK_DOWN | ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK, ui::EF_CAPS_LOCK_DOWN | ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK)); #if defined(USE_X11) // Confirm that calling RewriteForTesting() does not change the state of // |ime_keyboard|. In this case, X Window system itself should change the // Caps Lock state, not ash::EventRewriter. EXPECT_FALSE(ime_keyboard.caps_lock_is_enabled_); #elif defined(USE_OZONE) // Under Ozone the rewriter is responsible for changing the caps lock // state when the final key is Caps Lock, regardless of whether the // initial key is Caps Lock. EXPECT_TRUE(ime_keyboard.caps_lock_is_enabled_); #endif // Release Caps Lock (on an external keyboard). EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK, ui::EF_NONE, ui::DomKey::CAPS_LOCK), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK, ui::EF_NONE, ui::DomKey::CAPS_LOCK)); #if defined(USE_X11) EXPECT_FALSE(ime_keyboard.caps_lock_is_enabled_); #elif defined(USE_OZONE) EXPECT_TRUE(ime_keyboard.caps_lock_is_enabled_); #endif } TEST_F(EventRewriterTest, TestRewriteCapsLock) { syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); chromeos::input_method::FakeImeKeyboard ime_keyboard; EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); EXPECT_FALSE(ime_keyboard.caps_lock_is_enabled_); // On Chrome OS, CapsLock is mapped to F16 with Mod3Mask. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK, ui::EF_CAPS_LOCK_DOWN | ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_F16, ui::DomCode::F16, ui::EF_MOD3_DOWN, ui::DomKey::F16)); EXPECT_TRUE(ime_keyboard.caps_lock_is_enabled_); } TEST_F(EventRewriterTest, TestRewriteDiamondKey) { syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); chromeos::input_method::FakeImeKeyboard ime_keyboard; EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); KeyTestCase tests[] = { // F15 should work as Ctrl when --has-chromeos-diamond-key is not // specified. {ui::ET_KEY_PRESSED, {ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15}, {ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN, ui::DomKey::CONTROL}}, {ui::ET_KEY_RELEASED, {ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15}, {ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_NONE, ui::DomKey::CONTROL}}, // However, Mod2Mask should not be rewritten to CtrlMask when // --has-chromeos-diamond-key is not specified. {ui::ET_KEY_PRESSED, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character}, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character}}, }; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteDiamondKeyWithFlag) { const base::CommandLine original_cl(*base::CommandLine::ForCurrentProcess()); base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( chromeos::switches::kHasChromeOSDiamondKey, ""); syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); chromeos::input_method::FakeImeKeyboard ime_keyboard; EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); // By default, F15 should work as Control. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN, ui::DomKey::CONTROL), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15)); // Check that Control is applied to a subsequent key press. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_CONTROL_DOWN, ui::DomKey::Constant<'a'>::Character), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character)); // Release F15 EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_NONE, ui::DomKey::CONTROL), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_RELEASED, ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15)); // Check that Control is no longer applied to a subsequent key press. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character)); IntegerPrefMember diamond; diamond.Init(prefs::kLanguageRemapDiamondKeyTo, &prefs); diamond.SetValue(chromeos::input_method::kVoidKey); EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_NONE, ui::DomKey::UNIDENTIFIED), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15)); // Check that no modifier is applied to another key. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character)); diamond.SetValue(chromeos::input_method::kControlKey); EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN, ui::DomKey::CONTROL), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15)); // Check that Control is applied to a subsequent key press. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_CONTROL_DOWN, ui::DomKey::Constant<'a'>::Character), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character)); // Release F15 EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_NONE, ui::DomKey::CONTROL), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_RELEASED, ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15)); // Check that Control is no longer applied to a subsequent key press. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character)); diamond.SetValue(chromeos::input_method::kAltKey); EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN, ui::DomKey::ALT), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15)); // Check that Alt is applied to a subsequent key press. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_ALT_DOWN, ui::DomKey::Constant<'a'>::Character), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character)); // Release F15 EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_NONE, ui::DomKey::ALT), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_RELEASED, ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15)); // Check that Alt is no longer applied to a subsequent key press. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character)); diamond.SetValue(chromeos::input_method::kCapsLockKey); EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK, ui::EF_CAPS_LOCK_DOWN | ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15)); // Check that Caps is applied to a subsequent key press. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_CAPS_LOCK_DOWN | ui::EF_MOD3_DOWN, ui::DomKey::Constant<'A'>::Character), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character)); // Release F15 EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK, ui::EF_NONE, ui::DomKey::CAPS_LOCK), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_RELEASED, ui::VKEY_F15, ui::DomCode::F15, ui::EF_NONE, ui::DomKey::F15)); // Check that Control is no longer applied to a subsequent key press. EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character)); *base::CommandLine::ForCurrentProcess() = original_cl; } TEST_F(EventRewriterTest, TestRewriteCapsLockToControl) { // Remap CapsLock to Control. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember control; control.Init(prefs::kLanguageRemapCapsLockKeyTo, &prefs); control.SetValue(chromeos::input_method::kControlKey); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { // 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::KEY_A, ui::EF_MOD3_DOWN, ui::DomKey::Constant<'a'>::Character}, {ui::VKEY_A, ui::DomCode::KEY_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::KEY_A, ui::EF_CONTROL_DOWN | ui::EF_MOD3_DOWN, ui::DomKey::Constant<'a'>::Character}, {ui::VKEY_A, ui::DomCode::KEY_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::KEY_A, ui::EF_ALT_DOWN | ui::EF_MOD3_DOWN, ui::DomKey::Constant<'a'>::Character}, {ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::Constant<'a'>::Character}}, }; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteCapsLockMod3InUse) { // Remap CapsLock to Control. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember control; control.Init(prefs::kLanguageRemapCapsLockKeyTo, &prefs); control.SetValue(chromeos::input_method::kControlKey); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); 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::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character), GetRewrittenEventAsString(&rewriter, ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, ui::EF_NONE, ui::DomKey::Constant<'a'>::Character)); input_method_manager_mock_->set_mod3_used(false); } TEST_F(EventRewriterTest, TestRewriteExtendedKeys) { syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_last_keyboard_device_id_for_testing(kKeyboardDeviceId); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { // 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}}, // Search+Alt+Up -> Alt+Up {ui::ET_KEY_PRESSED, {ui::VKEY_UP, ui::DomCode::ARROW_UP, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ARROW_UP}, {ui::VKEY_UP, ui::DomCode::ARROW_UP, ui::EF_ALT_DOWN, ui::DomKey::ARROW_UP}}, // Search+Alt+Down -> Alt+Down {ui::ET_KEY_PRESSED, {ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ARROW_DOWN}, {ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN, ui::EF_ALT_DOWN, ui::DomKey::ARROW_DOWN}}, // Search+Ctrl+Alt+Up -> Search+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}}, // 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+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 -> 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 -> 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}}, // 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}}}; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteFunctionKeys) { syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { // 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 -> Launch App 2 {ui::ET_KEY_PRESSED, {ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4}, {ui::VKEY_MEDIA_LAUNCH_APP2, 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_MEDIA_LAUNCH_APP2, 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_MEDIA_LAUNCH_APP2, 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::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::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::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::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::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::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::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::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::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}}, // 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}}, // The number row should be rewritten as the F 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}}, // 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}}}; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteExtendedKeysWithSearchRemapped) { // Remap Search to Control. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember search; search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kControlKey); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { // Alt+Search+Down -> End {ui::ET_KEY_PRESSED, {ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::ARROW_DOWN}, {ui::VKEY_END, ui::DomCode::END, ui::EF_NONE, ui::DomKey::END}}, // Shift+Alt+Search+Down -> Shift+End {ui::ET_KEY_PRESSED, {ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::DomKey::ARROW_DOWN}, {ui::VKEY_END, ui::DomCode::END, ui::EF_SHIFT_DOWN, ui::DomKey::END}}, }; for (const auto& test : tests) { CheckKeyTestCase(&rewriter, test); } } TEST_F(EventRewriterTest, TestRewriteKeyEventSentByXSendEvent) { // Remap Control to Alt. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember control; control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); // 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()); scoped_ptr new_event; // Control should NOT be remapped to Alt if EF_FINAL is set. EXPECT_EQ(ui::EVENT_REWRITE_CONTINUE, rewriter.RewriteEvent(keyevent, &new_event)); EXPECT_FALSE(new_event); } #if defined(USE_X11) // Send left control press, using XI2 native events. { ui::ScopedXI2Event xev; xev.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, 0); XEvent* xevent = xev; xevent->xkey.keycode = XKeysymToKeycode(gfx::GetXDisplay(), XK_Control_L); xevent->xkey.send_event = True; // XSendEvent() always does this. ui::KeyEvent keyevent(xev); scoped_ptr new_event; // Control should NOT be remapped to Alt if send_event // flag in the event is True. EXPECT_EQ(ui::EVENT_REWRITE_CONTINUE, rewriter.RewriteEvent(keyevent, &new_event)); EXPECT_FALSE(new_event); } #endif } TEST_F(EventRewriterTest, TestRewriteNonNativeEvent) { // Remap Control to Alt. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember control; control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); const int kTouchId = 2; gfx::Point location(0, 0); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, location, kTouchId, base::TimeDelta()); press.set_flags(ui::EF_CONTROL_DOWN); #if defined(USE_X11) ui::UpdateX11EventForFlags(&press); #endif scoped_ptr new_event; rewriter.RewriteEvent(press, &new_event); EXPECT_TRUE(new_event); // Control should be remapped to Alt. EXPECT_EQ(ui::EF_ALT_DOWN, new_event->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(ScopedVector* events) { events->clear(); events->swap(events_); } private: // ui::EventProcessor overrides: ui::EventDispatchDetails OnEventFromSource(ui::Event* event) override { if (event->IsKeyEvent()) { events_.push_back(new ui::KeyEvent(*static_cast(event))); } else if (event->IsMouseWheelEvent()) { events_.push_back( new ui::MouseWheelEvent(*static_cast(event))); } else if (event->IsMouseEvent()) { events_.push_back( new ui::MouseEvent(*static_cast(event))); } return ui::EventDispatchDetails(); } ScopedVector 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::EventProcessor* GetEventProcessor() override { return processor_; } ui::EventDispatchDetails Send(ui::Event* event) { return SendEventToProcessor(event); } private: ui::EventProcessor* processor_; }; // Tests of event rewriting that depend on the Ash window manager. class EventRewriterAshTest : public ash::test::AshTestBase { public: EventRewriterAshTest() : source_(&buffer_), fake_user_manager_(new user_manager::FakeUserManager), user_manager_enabler_(fake_user_manager_) {} ~EventRewriterAshTest() override {} bool RewriteFunctionKeys(const ui::Event& event, scoped_ptr* rewritten_event) { return rewriter_->RewriteEvent(event, rewritten_event); } 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) { ui::KeyEvent press(type, key_code, code, ui::EF_NONE, 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: syncable_prefs::TestingPrefServiceSyncable* prefs() { return &prefs_; } void PopEvents(ScopedVector* events) { buffer_.PopEvents(events); } void SetUp() override { AshTestBase::SetUp(); sticky_keys_controller_ = ash::Shell::GetInstance()->sticky_keys_controller(); rewriter_.reset(new EventRewriter(sticky_keys_controller_)); chromeos::Preferences::RegisterProfilePrefs(prefs_.registry()); rewriter_->set_pref_service_for_testing(&prefs_); #if defined(USE_X11) ui::SetUpTouchPadForTest(kTouchPadDeviceId); #endif source_.AddEventRewriter(rewriter_.get()); sticky_keys_controller_->Enable(true); } void TearDown() override { rewriter_.reset(); AshTestBase::TearDown(); } protected: ash::StickyKeysController* sticky_keys_controller_; private: scoped_ptr rewriter_; EventBuffer buffer_; TestEventSource source_; user_manager::FakeUserManager* fake_user_manager_; // Not owned. chromeos::ScopedUserManagerEnabler user_manager_enabler_; syncable_prefs::TestingPrefServiceSyncable prefs_; DISALLOW_COPY_AND_ASSIGN(EventRewriterAshTest); }; TEST_F(EventRewriterAshTest, TopRowKeysAreFunctionKeys) { scoped_ptr window(CreateTestWindowInShellWithId(1)); ash::wm::WindowState* window_state = ash::wm::GetWindowState(window.get()); window_state->Activate(); ScopedVector 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()); // Simulate an apps v2 window that has requested top row keys as function // keys. The event should not be rewritten. window_state->set_top_row_keys_are_function_keys(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), GetKeyEventAsString(*static_cast(events[0]))); // 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); window_state->set_top_row_keys_are_function_keys(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_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1), GetKeyEventAsString(*static_cast(events[0]))); // 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), GetKeyEventAsString(*static_cast(events[0]))); } TEST_F(EventRewriterTest, TestRewrittenModifierClick) { #if defined(USE_X11) std::vector device_list; device_list.push_back(10); ui::TouchFactory::GetInstance()->SetPointerDeviceForTest(device_list); // Remap Control to Alt. syncable_prefs::TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); IntegerPrefMember control; control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); EventRewriter rewriter(NULL); rewriter.KeyboardDeviceAddedForTesting(kKeyboardDeviceId, "PC Keyboard"); rewriter.set_pref_service_for_testing(&prefs); // Check that Control + Left Button is converted (via Alt + Left Button) // to Right Button. ui::ScopedXI2Event xev; xev.InitGenericButtonEvent(10, ui::ET_MOUSE_PRESSED, gfx::Point(), ui::EF_LEFT_MOUSE_BUTTON | ui::EF_CONTROL_DOWN); ui::MouseEvent press(xev); // Sanity check. EXPECT_EQ(ui::ET_MOUSE_PRESSED, press.type()); EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON | ui::EF_CONTROL_DOWN, press.flags()); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, press, &new_event); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result->flags()); EXPECT_FALSE(ui::EF_LEFT_MOUSE_BUTTON & result->flags()); EXPECT_FALSE(ui::EF_CONTROL_DOWN & result->flags()); EXPECT_FALSE(ui::EF_ALT_DOWN & result->flags()); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result->flags()); #endif } TEST_F(EventRewriterTest, DontRewriteIfNotRewritten) { // TODO(kpschoedel): pending changes for crbug.com/360377 // to |chromeos::EventRewriter::RewriteLocatedEvent() #if defined(USE_X11) std::vector device_list; device_list.push_back(10); device_list.push_back(11); ui::TouchFactory::GetInstance()->SetPointerDeviceForTest(device_list); #endif syncable_prefs::TestingPrefServiceSyncable prefs; EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); const int kLeftAndAltFlag = ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN; // Test Alt + Left click. { ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(), kLeftAndAltFlag, ui::EF_LEFT_MOUSE_BUTTON); ui::EventTestApi test_press(&press); test_press.set_source_device_id(10); // Sanity check. EXPECT_EQ(ui::ET_MOUSE_PRESSED, press.type()); EXPECT_EQ(kLeftAndAltFlag, press.flags()); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, press, &new_event); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result->flags()); EXPECT_FALSE(kLeftAndAltFlag & 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(), kLeftAndAltFlag, ui::EF_LEFT_MOUSE_BUTTON); ui::EventTestApi test_release(&release); test_release.set_source_device_id(10); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, release, &new_event); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result->flags()); EXPECT_FALSE(kLeftAndAltFlag & result->flags()); EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result->changed_button_flags()); } #if defined(USE_X11) // Test Alt + Left click, using XI2 native events. { ui::ScopedXI2Event xev; xev.InitGenericButtonEvent(10, ui::ET_MOUSE_PRESSED, gfx::Point(), kLeftAndAltFlag); ui::MouseEvent press(xev); // Sanity check. EXPECT_EQ(ui::ET_MOUSE_PRESSED, press.type()); EXPECT_EQ(kLeftAndAltFlag, press.flags()); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, press, &new_event); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result->flags()); EXPECT_FALSE(kLeftAndAltFlag & result->flags()); EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result->changed_button_flags()); } { ui::ScopedXI2Event xev; xev.InitGenericButtonEvent(10, ui::ET_MOUSE_RELEASED, gfx::Point(), kLeftAndAltFlag); ui::MouseEvent release(xev); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, release, &new_event); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result->flags()); EXPECT_FALSE(kLeftAndAltFlag & result->flags()); EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result->changed_button_flags()); } #endif // No ALT in frst 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(10); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, press, &new_event); 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(), kLeftAndAltFlag, ui::EF_LEFT_MOUSE_BUTTON); ui::EventTestApi test_release(&release); test_release.set_source_device_id(10); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, release, &new_event); EXPECT_TRUE((ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN) & result->flags()); EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result->changed_button_flags()); } #if defined(USE_X11) // No ALT in frst click, using XI2 native events. { ui::ScopedXI2Event xev; xev.InitGenericButtonEvent(10, ui::ET_MOUSE_PRESSED, gfx::Point(), ui::EF_LEFT_MOUSE_BUTTON); ui::MouseEvent press(xev); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, press, &new_event); EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & result->flags()); EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result->changed_button_flags()); } { ui::ScopedXI2Event xev; xev.InitGenericButtonEvent(10, ui::ET_MOUSE_RELEASED, gfx::Point(), kLeftAndAltFlag); ui::MouseEvent release(xev); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, release, &new_event); EXPECT_TRUE((ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN) & result->flags()); EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result->changed_button_flags()); } #endif // ALT on different device. { ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(), kLeftAndAltFlag, ui::EF_LEFT_MOUSE_BUTTON); ui::EventTestApi test_press(&press); test_press.set_source_device_id(11); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, press, &new_event); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result->flags()); EXPECT_FALSE(kLeftAndAltFlag & 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(), kLeftAndAltFlag, ui::EF_LEFT_MOUSE_BUTTON); ui::EventTestApi test_release(&release); test_release.set_source_device_id(10); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, release, &new_event); EXPECT_TRUE((ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN) & 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(), kLeftAndAltFlag, ui::EF_LEFT_MOUSE_BUTTON); ui::EventTestApi test_release(&release); test_release.set_source_device_id(11); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, release, &new_event); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result->flags()); EXPECT_FALSE(kLeftAndAltFlag & result->flags()); EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result->changed_button_flags()); } #if defined(USE_X11) // ALT on different device, using XI2 native events. { ui::ScopedXI2Event xev; xev.InitGenericButtonEvent(11, ui::ET_MOUSE_PRESSED, gfx::Point(), kLeftAndAltFlag); ui::MouseEvent press(xev); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, press, &new_event); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result->flags()); EXPECT_FALSE(kLeftAndAltFlag & result->flags()); EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result->changed_button_flags()); } { ui::ScopedXI2Event xev; xev.InitGenericButtonEvent(10, ui::ET_MOUSE_RELEASED, gfx::Point(), kLeftAndAltFlag); ui::MouseEvent release(xev); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, release, &new_event); EXPECT_TRUE((ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN) & result->flags()); EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result->changed_button_flags()); } { ui::ScopedXI2Event xev; xev.InitGenericButtonEvent(11, ui::ET_MOUSE_RELEASED, gfx::Point(), kLeftAndAltFlag); ui::MouseEvent release(xev); scoped_ptr new_event; const ui::MouseEvent* result = RewriteMouseButtonEvent(&rewriter, release, &new_event); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & result->flags()); EXPECT_FALSE(kLeftAndAltFlag & result->flags()); EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result->changed_button_flags()); } #endif } TEST_F(EventRewriterAshTest, StickyKeyEventDispatchImpl) { // Test the actual key event dispatch implementation. ScopedVector 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(events[0])->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::KEY_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(events[0])->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(events[1])->key_code()); // Test key release event is not modified. ui::KeyEvent release(ui::ET_KEY_RELEASED, ui::VKEY_C, ui::DomCode::KEY_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(events[0])->key_code()); EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN); } TEST_F(EventRewriterAshTest, MouseEventDispatchImpl) { ScopedVector 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(events[1])->key_code()); } TEST_F(EventRewriterAshTest, MouseWheelEventDispatchImpl) { ScopedVector 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::MouseEvent mev(ui::ET_MOUSEWHEEL, location, location, ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); ui::MouseWheelEvent positive(mev, 0, ui::MouseWheelEvent::kWheelDelta); 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(events[1])->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(mev, 0, -ui::MouseWheelEvent::kWheelDelta); 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(events[1])->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_); } ash::StickyKeysOverlay* overlay_; }; TEST_F(StickyKeysOverlayTest, OneModifierEnabled) { EXPECT_FALSE(overlay_->is_visible()); EXPECT_EQ(ash::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(ash::STICKY_KEY_STATE_ENABLED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); // Pressing a normal key should hide overlay. SendActivateStickyKeyPattern(ui::VKEY_T, ui::DomCode::KEY_T, ui::DomKey::Constant<'t'>::Character); EXPECT_FALSE(overlay_->is_visible()); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); } TEST_F(StickyKeysOverlayTest, TwoModifiersEnabled) { EXPECT_FALSE(overlay_->is_visible()); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); EXPECT_EQ(ash::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(ash::STICKY_KEY_STATE_ENABLED, overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); // Pressing a normal key should hide overlay. SendActivateStickyKeyPattern(ui::VKEY_N, ui::DomCode::KEY_N, ui::DomKey::Constant<'n'>::Character); EXPECT_FALSE(overlay_->is_visible()); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); } TEST_F(StickyKeysOverlayTest, LockedModifier) { EXPECT_FALSE(overlay_->is_visible()); EXPECT_EQ(ash::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(ash::STICKY_KEY_STATE_LOCKED, overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); // Pressing a normal key should not hide overlay. SendActivateStickyKeyPattern(ui::VKEY_D, ui::DomCode::KEY_D, ui::DomKey::Constant<'d'>::Character); EXPECT_TRUE(overlay_->is_visible()); EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED, overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); } TEST_F(StickyKeysOverlayTest, LockedAndNormalModifier) { EXPECT_FALSE(overlay_->is_visible()); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); EXPECT_EQ(ash::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(ash::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(ash::STICKY_KEY_STATE_LOCKED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); EXPECT_EQ(ash::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::KEY_D, ui::DomKey::Constant<'d'>::Character); EXPECT_TRUE(overlay_->is_visible()); EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); } TEST_F(StickyKeysOverlayTest, ModifiersDisabled) { EXPECT_FALSE(overlay_->is_visible()); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); EXPECT_EQ(ash::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::OS_LEFT, ui::DomKey::OS); SendActivateStickyKeyPattern(ui::VKEY_COMMAND, ui::DomCode::OS_LEFT, ui::DomKey::OS); EXPECT_TRUE(overlay_->is_visible()); EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED, overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED, overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); EXPECT_EQ(ash::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::OS_LEFT, ui::DomKey::OS); EXPECT_FALSE(overlay_->is_visible()); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); EXPECT_EQ(ash::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)); } } // namespace chromeos