input_browsertest.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <fuchsia/input/virtualkeyboard/cpp/fidl.h>
  5. #include <fuchsia/ui/input3/cpp/fidl.h>
  6. #include <fuchsia/ui/input3/cpp/fidl_test_base.h>
  7. #include <memory>
  8. #include "base/fuchsia/scoped_service_binding.h"
  9. #include "base/fuchsia/test_component_context_for_process.h"
  10. #include "base/test/scoped_feature_list.h"
  11. #include "content/public/test/browser_test.h"
  12. #include "content/public/test/browser_test_utils.h"
  13. #include "fuchsia_web/common/test/frame_test_util.h"
  14. #include "fuchsia_web/common/test/test_navigation_listener.h"
  15. #include "fuchsia_web/webengine/browser/context_impl.h"
  16. #include "fuchsia_web/webengine/features.h"
  17. #include "fuchsia_web/webengine/test/frame_for_test.h"
  18. #include "fuchsia_web/webengine/test/scenic_test_helper.h"
  19. #include "fuchsia_web/webengine/test/scoped_connection_checker.h"
  20. #include "fuchsia_web/webengine/test/test_data.h"
  21. #include "fuchsia_web/webengine/test/web_engine_browser_test.h"
  22. #include "testing/gtest/include/gtest/gtest.h"
  23. using fuchsia::input::Key;
  24. using fuchsia::ui::input3::KeyEvent;
  25. using fuchsia::ui::input3::KeyEventType;
  26. using fuchsia::ui::input3::KeyMeaning;
  27. using fuchsia::ui::input3::NonPrintableKey;
  28. namespace {
  29. const char kKeyDown[] = "keydown";
  30. const char kKeyPress[] = "keypress";
  31. const char kKeyUp[] = "keyup";
  32. const char kKeyDicts[] = "keyDicts";
  33. // Returns a KeyEvent with |key_meaning| set based on the supplied codepoint,
  34. // the |key| field left not set.
  35. KeyEvent CreateCharacterKeyEvent(uint32_t codepoint, KeyEventType event_type) {
  36. KeyEvent key_event;
  37. fuchsia::ui::input3::KeyMeaning meaning;
  38. meaning.set_codepoint(codepoint);
  39. key_event.set_key_meaning(std::move(meaning));
  40. key_event.set_type(event_type);
  41. key_event.set_timestamp(base::TimeTicks::Now().ToZxTime());
  42. return key_event;
  43. }
  44. // Returns a KeyEvent with only the |key| field set, and |key_meaning| not set.
  45. KeyEvent CreateKeyEventNoMeaning(Key key, KeyEventType event_type) {
  46. KeyEvent key_event;
  47. key_event.set_timestamp(base::TimeTicks::Now().ToZxTime());
  48. key_event.set_type(event_type);
  49. key_event.set_key(key);
  50. return key_event;
  51. }
  52. // Returns a KeyEvent with both |key| and |key_meaning| set.
  53. KeyEvent CreateKeyEvent(Key key,
  54. KeyMeaning key_meaning,
  55. KeyEventType event_type) {
  56. KeyEvent key_event = CreateKeyEventNoMeaning(key, event_type);
  57. key_event.set_key_meaning(std::move(key_meaning));
  58. return key_event;
  59. }
  60. KeyEvent CreateKeyEvent(Key key, uint32_t codepoint, KeyEventType event_type) {
  61. return CreateKeyEvent(key, KeyMeaning::WithCodepoint(std::move(codepoint)),
  62. event_type);
  63. }
  64. KeyEvent CreateKeyEvent(Key key,
  65. NonPrintableKey non_printable_key,
  66. KeyEventType event_type) {
  67. return CreateKeyEvent(
  68. key, KeyMeaning::WithNonPrintableKey(std::move(non_printable_key)),
  69. event_type);
  70. }
  71. base::Value ExpectedKeyValue(base::StringPiece code,
  72. base::StringPiece key,
  73. base::StringPiece type) {
  74. base::Value::Dict expected;
  75. expected.Set("code", code);
  76. expected.Set("key", key);
  77. expected.Set("type", type);
  78. return base::Value(std::move(expected));
  79. }
  80. class FakeKeyboard : public fuchsia::ui::input3::testing::Keyboard_TestBase {
  81. public:
  82. explicit FakeKeyboard(sys::OutgoingDirectory* additional_services)
  83. : binding_(additional_services, this) {}
  84. ~FakeKeyboard() override = default;
  85. FakeKeyboard(const FakeKeyboard&) = delete;
  86. FakeKeyboard& operator=(const FakeKeyboard&) = delete;
  87. base::ScopedServiceBinding<fuchsia::ui::input3::Keyboard>* binding() {
  88. return &binding_;
  89. }
  90. // Sends |key_event| to |listener_|;
  91. void SendKeyEvent(KeyEvent key_event) {
  92. listener_->OnKeyEvent(std::move(key_event),
  93. [num_sent_events = num_sent_events_,
  94. this](fuchsia::ui::input3::KeyEventStatus status) {
  95. ASSERT_EQ(num_acked_events_, num_sent_events)
  96. << "Key events are acked out of order";
  97. num_acked_events_++;
  98. });
  99. num_sent_events_++;
  100. }
  101. // fuchsia::ui::input3::Keyboard implementation.
  102. void AddListener(
  103. fuchsia::ui::views::ViewRef view_ref,
  104. fidl::InterfaceHandle<::fuchsia::ui::input3::KeyboardListener> listener,
  105. AddListenerCallback callback) final {
  106. // This implementation is only set up to have up to one listener.
  107. EXPECT_FALSE(listener_);
  108. listener_ = listener.Bind();
  109. callback();
  110. }
  111. void NotImplemented_(const std::string& name) final {
  112. NOTIMPLEMENTED() << name;
  113. }
  114. private:
  115. fuchsia::ui::input3::KeyboardListenerPtr listener_;
  116. base::ScopedServiceBinding<fuchsia::ui::input3::Keyboard> binding_;
  117. // Counters to make sure key events are acked in order.
  118. int num_sent_events_ = 0;
  119. int num_acked_events_ = 0;
  120. };
  121. class KeyboardInputTest : public WebEngineBrowserTest {
  122. public:
  123. KeyboardInputTest() { set_test_server_root(base::FilePath(kTestServerRoot)); }
  124. ~KeyboardInputTest() override = default;
  125. KeyboardInputTest(const KeyboardInputTest&) = delete;
  126. KeyboardInputTest& operator=(const KeyboardInputTest&) = delete;
  127. protected:
  128. virtual void SetUpService() {
  129. keyboard_service_.emplace(component_context_->additional_services());
  130. }
  131. void SetUp() override {
  132. scoped_feature_list_.InitWithFeatures({features::kKeyboardInput}, {});
  133. WebEngineBrowserTest::SetUp();
  134. }
  135. void SetUpOnMainThread() override {
  136. WebEngineBrowserTest::SetUpOnMainThread();
  137. ASSERT_TRUE(embedded_test_server()->Start());
  138. fuchsia::web::CreateFrameParams params;
  139. frame_for_test_ = FrameForTest::Create(context(), std::move(params));
  140. // Set up services needed for the test. The keyboard service is included in
  141. // the allowed services by default. The real service needs to be removed so
  142. // it can be replaced by this fake implementation.
  143. component_context_.emplace(
  144. base::TestComponentContextForProcess::InitialState::kCloneAll);
  145. component_context_->additional_services()
  146. ->RemovePublicService<fuchsia::ui::input3::Keyboard>();
  147. SetUpService();
  148. virtual_keyboard_checker_.emplace(
  149. component_context_->additional_services());
  150. fuchsia::web::NavigationControllerPtr controller;
  151. frame_for_test_.ptr()->GetNavigationController(controller.NewRequest());
  152. const GURL test_url(embedded_test_server()->GetURL("/keyevents.html"));
  153. EXPECT_TRUE(LoadUrlAndExpectResponse(
  154. controller.get(), fuchsia::web::LoadUrlParams(), test_url.spec()));
  155. frame_for_test_.navigation_listener().RunUntilUrlEquals(test_url);
  156. fuchsia::web::FramePtr* frame_ptr = &(frame_for_test_.ptr());
  157. scenic_test_helper_.CreateScenicView(
  158. context_impl()->GetFrameImplForTest(frame_ptr), frame_for_test_.ptr());
  159. scenic_test_helper_.SetUpViewForInteraction(
  160. context_impl()->GetFrameImplForTest(frame_ptr)->web_contents());
  161. }
  162. // The tests expect to have input processed immediately, even if the
  163. // content has not been displayed yet. That's fine for the test, but
  164. // we need to explicitly allow it.
  165. void SetUpCommandLine(base::CommandLine* command_line) override {
  166. command_line->AppendSwitch("allow-pre-commit-input");
  167. }
  168. template <typename... Args>
  169. void ExpectKeyEventsEqual(Args... events) {
  170. base::Value::List expected;
  171. content::ConvertToBaseValueList(expected, std::forward<Args>(events)...);
  172. frame_for_test_.navigation_listener().RunUntilTitleEquals(
  173. base::NumberToString(expected.size()));
  174. absl::optional<base::Value> actual =
  175. ExecuteJavaScript(frame_for_test_.ptr().get(), kKeyDicts);
  176. EXPECT_EQ(*actual, base::Value(std::move(expected)));
  177. }
  178. // Used to publish fake services.
  179. absl::optional<base::TestComponentContextForProcess> component_context_;
  180. FrameForTest frame_for_test_;
  181. ScenicTestHelper scenic_test_helper_;
  182. absl::optional<FakeKeyboard> keyboard_service_;
  183. base::test::ScopedFeatureList scoped_feature_list_;
  184. absl::optional<
  185. NeverConnectedChecker<fuchsia::input::virtualkeyboard::ControllerCreator>>
  186. virtual_keyboard_checker_;
  187. };
  188. // Check that printable keys are sent and received correctly.
  189. IN_PROC_BROWSER_TEST_F(KeyboardInputTest, PrintableKeys) {
  190. // Send key press events from the Fuchsia keyboard service.
  191. // Pressing character keys will generate a JavaScript keydown event followed
  192. // by a keypress event. Releasing any key generates a keyup event.
  193. keyboard_service_->SendKeyEvent(
  194. CreateKeyEvent(Key::A, 'a', KeyEventType::PRESSED));
  195. keyboard_service_->SendKeyEvent(
  196. CreateKeyEvent(Key::KEY_8, '8', KeyEventType::PRESSED));
  197. keyboard_service_->SendKeyEvent(
  198. CreateKeyEvent(Key::KEY_8, '8', KeyEventType::RELEASED));
  199. keyboard_service_->SendKeyEvent(
  200. CreateKeyEvent(Key::A, 'a', KeyEventType::RELEASED));
  201. ExpectKeyEventsEqual(ExpectedKeyValue("KeyA", "a", kKeyDown),
  202. ExpectedKeyValue("KeyA", "a", kKeyPress),
  203. ExpectedKeyValue("Digit8", "8", kKeyDown),
  204. ExpectedKeyValue("Digit8", "8", kKeyPress),
  205. ExpectedKeyValue("Digit8", "8", kKeyUp),
  206. ExpectedKeyValue("KeyA", "a", kKeyUp));
  207. }
  208. // Check that character virtual keys are sent and received correctly.
  209. IN_PROC_BROWSER_TEST_F(KeyboardInputTest, Characters) {
  210. // Send key press events from the Fuchsia keyboard service.
  211. // Pressing character keys will generate a JavaScript keydown event followed
  212. // by a keypress event. Releasing any key generates a keyup event.
  213. keyboard_service_->SendKeyEvent(
  214. CreateCharacterKeyEvent('A', KeyEventType::PRESSED));
  215. keyboard_service_->SendKeyEvent(
  216. CreateCharacterKeyEvent('A', KeyEventType::RELEASED));
  217. keyboard_service_->SendKeyEvent(
  218. CreateCharacterKeyEvent('b', KeyEventType::PRESSED));
  219. ExpectKeyEventsEqual(
  220. ExpectedKeyValue("", "A", kKeyDown), ExpectedKeyValue("", "A", kKeyPress),
  221. ExpectedKeyValue("", "A", kKeyUp), ExpectedKeyValue("", "b", kKeyDown),
  222. ExpectedKeyValue("", "b", kKeyPress));
  223. }
  224. // Verify that character events are not affected by active modifiers.
  225. IN_PROC_BROWSER_TEST_F(KeyboardInputTest, ShiftCharacter) {
  226. // TODO(fxbug.dev/106600): Update the WithCodepoint(0)s when the platform is
  227. // fixed to provide valid KeyMeanings for these keys.
  228. keyboard_service_->SendKeyEvent(
  229. CreateKeyEvent(Key::LEFT_SHIFT, 0, KeyEventType::PRESSED));
  230. keyboard_service_->SendKeyEvent(
  231. CreateCharacterKeyEvent('a', KeyEventType::PRESSED));
  232. keyboard_service_->SendKeyEvent(
  233. CreateCharacterKeyEvent('a', KeyEventType::RELEASED));
  234. keyboard_service_->SendKeyEvent(
  235. CreateKeyEvent(Key::LEFT_SHIFT, 0, KeyEventType::RELEASED));
  236. ExpectKeyEventsEqual(
  237. ExpectedKeyValue("ShiftLeft", "Shift", kKeyDown),
  238. ExpectedKeyValue("", "a", kKeyDown), // Remains lowercase.
  239. ExpectedKeyValue("", "a", kKeyPress), // You guessed it! Still lowercase.
  240. ExpectedKeyValue("", "a", kKeyUp), // Wow, lowercase just won't quit.
  241. ExpectedKeyValue("ShiftLeft", "Shift", kKeyUp));
  242. }
  243. // Verifies that codepoints outside the 16-bit Unicode BMP are rejected.
  244. IN_PROC_BROWSER_TEST_F(KeyboardInputTest, CharacterInBmp) {
  245. const wchar_t kSigma = 0x03C3;
  246. keyboard_service_->SendKeyEvent(
  247. CreateCharacterKeyEvent(kSigma, KeyEventType::PRESSED));
  248. keyboard_service_->SendKeyEvent(
  249. CreateCharacterKeyEvent(kSigma, KeyEventType::RELEASED));
  250. std::string expected_utf8;
  251. ASSERT_TRUE(base::WideToUTF8(&kSigma, 1, &expected_utf8));
  252. ExpectKeyEventsEqual(ExpectedKeyValue("", expected_utf8, kKeyDown),
  253. ExpectedKeyValue("", expected_utf8, kKeyPress),
  254. ExpectedKeyValue("", expected_utf8, kKeyUp));
  255. }
  256. // Verifies that codepoints beyond the range of allowable UCS-2 values
  257. // are rejected.
  258. IN_PROC_BROWSER_TEST_F(KeyboardInputTest, CharacterBeyondBmp) {
  259. const uint32_t kRamenEmoji = 0x1F35C;
  260. keyboard_service_->SendKeyEvent(
  261. CreateCharacterKeyEvent(kRamenEmoji, KeyEventType::PRESSED));
  262. keyboard_service_->SendKeyEvent(
  263. CreateCharacterKeyEvent(kRamenEmoji, KeyEventType::RELEASED));
  264. keyboard_service_->SendKeyEvent(
  265. CreateCharacterKeyEvent('a', KeyEventType::PRESSED));
  266. keyboard_service_->SendKeyEvent(
  267. CreateCharacterKeyEvent('a', KeyEventType::RELEASED));
  268. ExpectKeyEventsEqual(ExpectedKeyValue("", "a", kKeyDown),
  269. ExpectedKeyValue("", "a", kKeyPress),
  270. ExpectedKeyValue("", "a", kKeyUp));
  271. }
  272. IN_PROC_BROWSER_TEST_F(KeyboardInputTest, ShiftPrintableKeys) {
  273. keyboard_service_->SendKeyEvent(
  274. CreateKeyEvent(Key::LEFT_SHIFT, 0, KeyEventType::PRESSED));
  275. keyboard_service_->SendKeyEvent(
  276. CreateKeyEvent(Key::B, 'B', KeyEventType::PRESSED));
  277. keyboard_service_->SendKeyEvent(
  278. CreateKeyEvent(Key::KEY_1, '!', KeyEventType::PRESSED));
  279. keyboard_service_->SendKeyEvent(
  280. CreateKeyEvent(Key::SPACE, ' ', KeyEventType::PRESSED));
  281. keyboard_service_->SendKeyEvent(
  282. CreateKeyEvent(Key::LEFT_SHIFT, 0, KeyEventType::RELEASED));
  283. keyboard_service_->SendKeyEvent(
  284. CreateKeyEvent(Key::DOT, '.', KeyEventType::PRESSED));
  285. // Note that non-character keys (e.g. shift, control) only generate key down
  286. // and key up web events. They do not generate key pressed events.
  287. ExpectKeyEventsEqual(ExpectedKeyValue("ShiftLeft", "Shift", kKeyDown),
  288. ExpectedKeyValue("KeyB", "B", kKeyDown),
  289. ExpectedKeyValue("KeyB", "B", kKeyPress),
  290. ExpectedKeyValue("Digit1", "!", kKeyDown),
  291. ExpectedKeyValue("Digit1", "!", kKeyPress),
  292. ExpectedKeyValue("Space", " ", kKeyDown),
  293. ExpectedKeyValue("Space", " ", kKeyPress),
  294. ExpectedKeyValue("ShiftLeft", "Shift", kKeyUp),
  295. ExpectedKeyValue("Period", ".", kKeyDown),
  296. ExpectedKeyValue("Period", ".", kKeyPress));
  297. }
  298. IN_PROC_BROWSER_TEST_F(KeyboardInputTest, ShiftNonPrintableKeys) {
  299. keyboard_service_->SendKeyEvent(
  300. CreateKeyEvent(Key::RIGHT_SHIFT, 0, KeyEventType::PRESSED));
  301. keyboard_service_->SendKeyEvent(CreateKeyEvent(
  302. Key::ENTER, NonPrintableKey::ENTER, KeyEventType::PRESSED));
  303. keyboard_service_->SendKeyEvent(
  304. CreateKeyEvent(Key::LEFT_CTRL, 0, KeyEventType::PRESSED));
  305. keyboard_service_->SendKeyEvent(
  306. CreateKeyEvent(Key::RIGHT_SHIFT, 0, KeyEventType::RELEASED));
  307. // Note that non-character keys (e.g. shift, control) only generate key down
  308. // and key up web events. They do not generate key pressed events.
  309. ExpectKeyEventsEqual(ExpectedKeyValue("ShiftRight", "Shift", kKeyDown),
  310. ExpectedKeyValue("Enter", "Enter", kKeyDown),
  311. ExpectedKeyValue("Enter", "Enter", kKeyPress),
  312. ExpectedKeyValue("ControlLeft", "Control", kKeyDown),
  313. ExpectedKeyValue("ShiftRight", "Shift", kKeyUp));
  314. }
  315. IN_PROC_BROWSER_TEST_F(KeyboardInputTest, Disconnect) {
  316. // Disconnect the keyboard service.
  317. keyboard_service_.reset();
  318. frame_for_test_.navigation_listener().RunUntilTitleEquals("loaded");
  319. // Make sure the page is still available and there are no crashes.
  320. EXPECT_TRUE(
  321. ExecuteJavaScript(frame_for_test_.ptr().get(), "true")->GetBool());
  322. }
  323. class KeyboardInputTestWithoutKeyboardFeature : public KeyboardInputTest {
  324. public:
  325. KeyboardInputTestWithoutKeyboardFeature() = default;
  326. ~KeyboardInputTestWithoutKeyboardFeature() override = default;
  327. protected:
  328. void SetUp() override {
  329. scoped_feature_list_.InitWithFeatures({}, {});
  330. WebEngineBrowserTest::SetUp();
  331. }
  332. void SetUpService() override {
  333. keyboard_input_checker_.emplace(component_context_->additional_services());
  334. }
  335. absl::optional<NeverConnectedChecker<fuchsia::ui::input3::Keyboard>>
  336. keyboard_input_checker_;
  337. };
  338. IN_PROC_BROWSER_TEST_F(KeyboardInputTestWithoutKeyboardFeature, NoFeature) {
  339. // Test will verify that |keyboard_input_checker_| never received a connection
  340. // request at teardown time.
  341. }
  342. } // namespace