| // Copyright 2018 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 <memory> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/events/event.h" |
| #include "ui/events/keyboard_hook.h" |
| #include "ui/events/keycodes/dom/dom_code.h" |
| #include "ui/events/keycodes/dom/keycode_converter.h" |
| #include "ui/events/keycodes/keyboard_codes.h" |
| #include "ui/events/win/keyboard_hook_win_base.h" |
| |
| namespace ui { |
| |
| class MediaKeyboardHookWinTest : public testing::Test { |
| public: |
| MediaKeyboardHookWinTest(); |
| ~MediaKeyboardHookWinTest() override; |
| |
| // testing::Test overrides. |
| void SetUp() override; |
| |
| void HandleKeyPress(KeyEvent* key_event); |
| |
| protected: |
| KeyboardHookWinBase* keyboard_hook() { return keyboard_hook_.get(); } |
| |
| uint32_t next_time_stamp() { return time_stamp_++; } |
| |
| std::vector<KeyEvent>* key_events() { return &key_events_; } |
| |
| // Used for sending key events which are handled by the hook. |
| void SendMediaKeyDownEvent(KeyboardCode key_code, |
| DomCode dom_code, |
| int repeat_count = 1); |
| void SendMediaKeyUpEvent(KeyboardCode key_code, DomCode dom_code); |
| |
| // Set the return value for the HandleKeyPress callback. |
| void StartHandlingKeys() { should_handle_keys_ = true; } |
| void StopHandlingKeys() { should_handle_keys_ = false; } |
| |
| private: |
| uint32_t time_stamp_ = 0; |
| std::unique_ptr<KeyboardHookWinBase> keyboard_hook_; |
| std::vector<KeyEvent> key_events_; |
| bool should_handle_keys_ = true; |
| |
| DISALLOW_COPY_AND_ASSIGN(MediaKeyboardHookWinTest); |
| }; |
| |
| MediaKeyboardHookWinTest::MediaKeyboardHookWinTest() = default; |
| |
| MediaKeyboardHookWinTest::~MediaKeyboardHookWinTest() = default; |
| |
| void MediaKeyboardHookWinTest::SetUp() { |
| keyboard_hook_ = KeyboardHookWinBase::CreateMediaKeyboardHookForTesting( |
| base::BindRepeating(&MediaKeyboardHookWinTest::HandleKeyPress, |
| base::Unretained(this))); |
| } |
| |
| void MediaKeyboardHookWinTest::HandleKeyPress(KeyEvent* key_event) { |
| key_events_.push_back(*key_event); |
| if (should_handle_keys_) |
| key_event->SetHandled(); |
| } |
| |
| void MediaKeyboardHookWinTest::SendMediaKeyDownEvent(KeyboardCode key_code, |
| DomCode dom_code, |
| int repeat_count /*=1*/) { |
| ASSERT_GT(repeat_count, 0); |
| // This should only be used when we're handling keys. |
| ASSERT_TRUE(should_handle_keys_); |
| |
| for (int i = 0; i < repeat_count; i++) { |
| ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYDOWN, key_code, |
| KeycodeConverter::DomCodeToNativeKeycode(dom_code), next_time_stamp())); |
| } |
| } |
| |
| void MediaKeyboardHookWinTest::SendMediaKeyUpEvent(KeyboardCode key_code, |
| DomCode dom_code) { |
| // This should only be used when we're handling keys. |
| ASSERT_TRUE(should_handle_keys_); |
| |
| ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYUP, key_code, KeycodeConverter::DomCodeToNativeKeycode(dom_code), |
| next_time_stamp())); |
| } |
| |
| namespace { |
| void VerifyKeyEvent(KeyEvent* key_event, |
| KeyboardCode non_located_key_code, |
| DomCode dom_code, |
| bool key_down, |
| bool is_repeat) { |
| if (key_down) { |
| ASSERT_EQ(key_event->type(), ET_KEY_PRESSED); |
| ASSERT_EQ(key_event->is_repeat(), is_repeat); |
| } else { |
| ASSERT_EQ(key_event->type(), ET_KEY_RELEASED); |
| ASSERT_FALSE(key_event->is_repeat()); |
| } |
| ASSERT_EQ(key_event->key_code(), non_located_key_code); |
| ASSERT_EQ(key_event->code(), dom_code); |
| } |
| } // namespace |
| |
| TEST_F(MediaKeyboardHookWinTest, SimpleKeypressTest) { |
| const KeyboardCode key_code = KeyboardCode::VKEY_MEDIA_PLAY_PAUSE; |
| const DomCode dom_code = DomCode::MEDIA_PLAY_PAUSE; |
| SendMediaKeyDownEvent(key_code, dom_code); |
| ASSERT_EQ(key_events()->size(), 1u); |
| SendMediaKeyUpEvent(key_code, dom_code); |
| ASSERT_EQ(key_events()->size(), 2u); |
| |
| KeyEvent down_event = key_events()->at(0); |
| ASSERT_NO_FATAL_FAILURE( |
| VerifyKeyEvent(&down_event, key_code, dom_code, true, false)); |
| |
| KeyEvent up_event = key_events()->at(1); |
| ASSERT_NO_FATAL_FAILURE( |
| VerifyKeyEvent(&up_event, key_code, dom_code, false, false)); |
| } |
| |
| TEST_F(MediaKeyboardHookWinTest, RepeatingKeypressTest) { |
| const int repeat_count = 10; |
| const KeyboardCode key_code = KeyboardCode::VKEY_MEDIA_PLAY_PAUSE; |
| const DomCode dom_code = DomCode::MEDIA_PLAY_PAUSE; |
| SendMediaKeyDownEvent(key_code, dom_code, repeat_count); |
| ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count); |
| SendMediaKeyUpEvent(key_code, dom_code); |
| ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1); |
| |
| bool should_repeat = false; |
| for (int i = 0; i < repeat_count; i++) { |
| KeyEvent event = key_events()->at(i); |
| ASSERT_NO_FATAL_FAILURE( |
| VerifyKeyEvent(&event, key_code, dom_code, true, should_repeat)); |
| should_repeat = true; |
| } |
| |
| KeyEvent up_event = key_events()->at(repeat_count); |
| ASSERT_NO_FATAL_FAILURE( |
| VerifyKeyEvent(&up_event, key_code, dom_code, false, false)); |
| } |
| |
| TEST_F(MediaKeyboardHookWinTest, UnhandledKeysArePropagated) { |
| StopHandlingKeys(); |
| |
| // Ensure media keys are propagated to the OS. |
| ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYDOWN, KeyboardCode::VKEY_MEDIA_STOP, |
| KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_STOP), |
| next_time_stamp())); |
| ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYUP, KeyboardCode::VKEY_MEDIA_STOP, |
| KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_STOP), |
| next_time_stamp())); |
| |
| StartHandlingKeys(); |
| |
| // Ensure media keys are not propagated to the OS. |
| ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYDOWN, KeyboardCode::VKEY_MEDIA_STOP, |
| KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_STOP), |
| next_time_stamp())); |
| ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYUP, KeyboardCode::VKEY_MEDIA_STOP, |
| KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_STOP), |
| next_time_stamp())); |
| } |
| |
| TEST_F(MediaKeyboardHookWinTest, NonInterceptedKeysTest) { |
| // Here we try a few keys we do not expect to be intercepted / handled. |
| ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYDOWN, KeyboardCode::VKEY_RSHIFT, |
| KeycodeConverter::DomCodeToNativeKeycode(DomCode::SHIFT_RIGHT), |
| next_time_stamp())); |
| ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYUP, KeyboardCode::VKEY_RSHIFT, |
| KeycodeConverter::DomCodeToNativeKeycode(DomCode::SHIFT_RIGHT), |
| next_time_stamp())); |
| |
| ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYDOWN, KeyboardCode::VKEY_ESCAPE, |
| KeycodeConverter::DomCodeToNativeKeycode(DomCode::ESCAPE), |
| next_time_stamp())); |
| ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYUP, KeyboardCode::VKEY_ESCAPE, |
| KeycodeConverter::DomCodeToNativeKeycode(DomCode::ESCAPE), |
| next_time_stamp())); |
| |
| ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYDOWN, KeyboardCode::VKEY_A, |
| KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A), |
| next_time_stamp())); |
| ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( |
| WM_KEYUP, KeyboardCode::VKEY_A, |
| KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A), |
| next_time_stamp())); |
| } |
| |
| } // namespace ui |