ime_service_unittest.cc 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. // Copyright 2018 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 "ash/services/ime/ime_service.h"
  5. #include "ash/constants/ash_features.h"
  6. #include "ash/services/ime/ime_decoder.h"
  7. #include "ash/services/ime/mock_input_channel.h"
  8. #include "ash/services/ime/public/mojom/input_engine.mojom.h"
  9. #include "ash/services/ime/public/mojom/input_method.mojom.h"
  10. #include "ash/services/ime/public/mojom/input_method_host.mojom.h"
  11. #include "base/bind.h"
  12. #include "base/strings/strcat.h"
  13. #include "base/strings/utf_string_conversions.h"
  14. #include "base/test/bind.h"
  15. #include "base/test/scoped_feature_list.h"
  16. #include "base/test/task_environment.h"
  17. #include "mojo/public/cpp/bindings/pending_receiver.h"
  18. #include "mojo/public/cpp/bindings/pending_remote.h"
  19. #include "mojo/public/cpp/bindings/receiver.h"
  20. #include "mojo/public/cpp/bindings/remote.h"
  21. #include "testing/gmock/include/gmock/gmock.h"
  22. #include "testing/gtest/include/gtest/gtest.h"
  23. using testing::_;
  24. namespace ash {
  25. namespace ime {
  26. namespace {
  27. const char kInvalidImeSpec[] = "ime_spec_never_support";
  28. constexpr char kValidImeSpec[] = "valid_spec";
  29. const std::vector<uint8_t> extra{0x66, 0x77, 0x88};
  30. void ConnectCallback(bool* success, bool result) {
  31. *success = result;
  32. }
  33. class TestDecoderState;
  34. // The fake decoder state has to be available globally because
  35. // ImeDecoder::EntryPoints is a list of stateless C functions, so the only way
  36. // to have a stateful fake is to have a global reference to it.
  37. TestDecoderState* g_test_decoder_state = nullptr;
  38. mojo::ScopedMessagePipeHandle MessagePipeHandleFromInt(uint32_t handle) {
  39. return mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(handle));
  40. }
  41. class TestDecoderState : public mojom::ConnectionFactory {
  42. public:
  43. bool InitializeConnectionFactory(uint32_t receiver_pipe_handle) {
  44. connection_factory_.reset();
  45. connection_factory_.Bind(mojo::PendingReceiver<mojom::ConnectionFactory>(
  46. MessagePipeHandleFromInt(receiver_pipe_handle)));
  47. connection_factory_.set_disconnect_handler(
  48. base::BindOnce(&mojo::Receiver<mojom::ConnectionFactory>::reset,
  49. base::Unretained(&connection_factory_)));
  50. return true;
  51. }
  52. bool IsConnected() { return connection_factory_.is_bound(); }
  53. // mojom::ConnectionFactory overrides
  54. void ConnectToInputMethod(
  55. const std::string& ime_spec,
  56. mojo::PendingAssociatedReceiver<ime::mojom::InputMethod> input_method,
  57. mojo::PendingAssociatedRemote<ime::mojom::InputMethodHost>
  58. input_method_host,
  59. mojom::InputMethodSettingsPtr settings,
  60. ConnectToInputMethodCallback callback) override {
  61. std::move(callback).Run(/*bound=*/false);
  62. }
  63. private:
  64. mojo::Receiver<ime::mojom::ConnectionFactory> connection_factory_{this};
  65. };
  66. class TestImeDecoder : public ImeDecoder {
  67. public:
  68. static TestImeDecoder* GetInstance() {
  69. static base::NoDestructor<TestImeDecoder> instance;
  70. return instance.get();
  71. }
  72. absl::optional<ImeDecoder::EntryPoints> MaybeLoadThenReturnEntryPoints()
  73. override {
  74. return entry_points_;
  75. }
  76. void ResetState() {
  77. delete g_test_decoder_state;
  78. g_test_decoder_state = new TestDecoderState();
  79. entry_points_ = {
  80. .init_proto_mode = [](ImeCrosPlatform* platform) {},
  81. .close_proto_mode = []() {},
  82. .supports =
  83. [](const char* ime_spec) {
  84. return strcmp(kInvalidImeSpec, ime_spec) != 0;
  85. },
  86. .activate_ime = [](const char* ime_spec,
  87. ImeClientDelegate* delegate) { return true; },
  88. .process = [](const uint8_t* data, size_t size) {},
  89. .init_mojo_mode = [](ImeCrosPlatform* platform) {},
  90. .close_mojo_mode = []() {},
  91. .connect_to_input_method =
  92. [](const char* ime_spec, uint32_t receiver_pipe_handle,
  93. uint32_t host_pipe_handle,
  94. uint32_t host_pipe_version) { return false; },
  95. .initialize_connection_factory =
  96. [](uint32_t receiver_pipe_handle) {
  97. return g_test_decoder_state->InitializeConnectionFactory(
  98. receiver_pipe_handle);
  99. },
  100. .is_input_method_connected =
  101. []() { return g_test_decoder_state->IsConnected(); },
  102. };
  103. }
  104. private:
  105. friend class base::NoDestructor<TestImeDecoder>;
  106. explicit TestImeDecoder() { ResetState(); }
  107. ~TestImeDecoder() override = default;
  108. absl::optional<ImeDecoder::EntryPoints> entry_points_;
  109. };
  110. struct MockInputMethodHost : public mojom::InputMethodHost {
  111. void CommitText(const std::u16string& text,
  112. mojom::CommitTextCursorBehavior cursor_behavior) override {
  113. last_commit = text;
  114. }
  115. void DEPRECATED_SetComposition(
  116. const std::u16string& text,
  117. std::vector<mojom::CompositionSpanPtr> spans) override {
  118. last_composition = text;
  119. }
  120. void SetComposition(const std::u16string& text,
  121. std::vector<mojom::CompositionSpanPtr> spans,
  122. uint32_t new_cursor_position) override {
  123. last_composition = text;
  124. }
  125. void SetCompositionRange(uint32_t start_index, uint32_t end_index) override {}
  126. void FinishComposition() override {}
  127. void DeleteSurroundingText(uint32_t num_before_cursor,
  128. uint32_t num_after_cursor) override {}
  129. void HandleAutocorrect(mojom::AutocorrectSpanPtr autocorrect_span) override {}
  130. void RequestSuggestions(mojom::SuggestionsRequestPtr request,
  131. RequestSuggestionsCallback callback) override {}
  132. void DisplaySuggestions(
  133. const std::vector<TextSuggestion>& suggestions) override {}
  134. void UpdateCandidatesWindow(mojom::CandidatesWindowPtr window) override {}
  135. void RecordUkm(mojom::UkmEntryPtr entry) override {}
  136. void ReportKoreanAction(mojom::KoreanAction action) override {}
  137. void ReportKoreanSettings(mojom::KoreanSettingsPtr settings) override {}
  138. void UpdateQuickSettings(
  139. mojom::InputMethodQuickSettingsPtr settings) override {}
  140. std::u16string last_commit;
  141. std::u16string last_composition;
  142. };
  143. class TestFieldTrialParamsRetriever : public FieldTrialParamsRetriever {
  144. public:
  145. explicit TestFieldTrialParamsRetriever() = default;
  146. ~TestFieldTrialParamsRetriever() override = default;
  147. TestFieldTrialParamsRetriever(const TestFieldTrialParamsRetriever&) = delete;
  148. TestFieldTrialParamsRetriever& operator=(
  149. const TestFieldTrialParamsRetriever&) = delete;
  150. std::string GetFieldTrialParamValueByFeature(
  151. const base::Feature& feature,
  152. const std::string& param_name) override {
  153. return base::StrCat({feature.name, "::", param_name});
  154. }
  155. };
  156. class ImeServiceTest : public testing::Test, public mojom::InputMethodHost {
  157. public:
  158. ImeServiceTest() = default;
  159. ImeServiceTest(const ImeServiceTest&) = delete;
  160. ImeServiceTest& operator=(const ImeServiceTest&) = delete;
  161. ~ImeServiceTest() override = default;
  162. void CommitText(const std::u16string& text,
  163. mojom::CommitTextCursorBehavior cursor_behavior) override {}
  164. void DEPRECATED_SetComposition(
  165. const std::u16string& text,
  166. std::vector<mojom::CompositionSpanPtr> spans) override {}
  167. void SetComposition(const std::u16string& text,
  168. std::vector<mojom::CompositionSpanPtr> spans,
  169. uint32_t new_cursor_position) override {}
  170. void SetCompositionRange(uint32_t start_index, uint32_t end_index) override {}
  171. void FinishComposition() override {}
  172. void DeleteSurroundingText(uint32_t num_before_cursor,
  173. uint32_t num_after_cursor) override {}
  174. void HandleAutocorrect(mojom::AutocorrectSpanPtr autocorrect_span) override {}
  175. void RequestSuggestions(mojom::SuggestionsRequestPtr request,
  176. RequestSuggestionsCallback callback) override {}
  177. void DisplaySuggestions(
  178. const std::vector<TextSuggestion>& suggestions) override {}
  179. void UpdateCandidatesWindow(mojom::CandidatesWindowPtr window) override {}
  180. void RecordUkm(mojom::UkmEntryPtr entry) override {}
  181. void ReportKoreanAction(mojom::KoreanAction action) override {}
  182. void ReportKoreanSettings(mojom::KoreanSettingsPtr settings) override {}
  183. void UpdateQuickSettings(
  184. mojom::InputMethodQuickSettingsPtr settings) override {}
  185. protected:
  186. void SetUp() override {
  187. service_ = std::make_unique<ImeService>(
  188. remote_service_.BindNewPipeAndPassReceiver(),
  189. TestImeDecoder::GetInstance(),
  190. std::make_unique<TestFieldTrialParamsRetriever>());
  191. remote_service_->BindInputEngineManager(
  192. remote_manager_.BindNewPipeAndPassReceiver());
  193. }
  194. void TearDown() override {
  195. service_.reset();
  196. TestImeDecoder::GetInstance()->ResetState();
  197. }
  198. mojo::Remote<mojom::ImeService> remote_service_;
  199. mojo::Remote<mojom::InputEngineManager> remote_manager_;
  200. protected:
  201. std::unique_ptr<ImeService> service_;
  202. private:
  203. base::test::TaskEnvironment task_environment_;
  204. };
  205. } // namespace
  206. // Tests that the service is instantiated and it will return false when
  207. // activating an IME engine with an invalid IME spec.
  208. TEST_F(ImeServiceTest, ConnectInvalidImeEngineDoesNotConnectRemote) {
  209. bool success = true;
  210. MockInputChannel test_channel;
  211. mojo::Remote<mojom::InputChannel> remote_engine;
  212. remote_manager_->ConnectToImeEngine(
  213. kInvalidImeSpec, remote_engine.BindNewPipeAndPassReceiver(),
  214. test_channel.CreatePendingRemote(), extra,
  215. base::BindOnce(&ConnectCallback, &success));
  216. remote_manager_.FlushForTesting();
  217. EXPECT_FALSE(success);
  218. EXPECT_FALSE(remote_engine.is_connected());
  219. }
  220. TEST_F(ImeServiceTest, ConnectToValidEngineConnectsRemote) {
  221. bool success = true;
  222. MockInputChannel test_channel;
  223. mojo::Remote<mojom::InputChannel> remote_engine;
  224. remote_manager_->ConnectToImeEngine(
  225. kValidImeSpec, remote_engine.BindNewPipeAndPassReceiver(),
  226. test_channel.CreatePendingRemote(), {},
  227. base::BindOnce(&ConnectCallback, &success));
  228. remote_manager_.FlushForTesting();
  229. EXPECT_TRUE(success);
  230. EXPECT_TRUE(remote_engine.is_connected());
  231. }
  232. TEST_F(ImeServiceTest, ConnectToImeEngineWillOverrideExistingImeEngine) {
  233. bool success1, success2 = true;
  234. MockInputChannel test_channel1, test_channel2;
  235. mojo::Remote<mojom::InputChannel> remote_engine1, remote_engine2;
  236. remote_manager_->ConnectToImeEngine(
  237. kValidImeSpec, remote_engine1.BindNewPipeAndPassReceiver(),
  238. test_channel1.CreatePendingRemote(), /*extra=*/{},
  239. base::BindOnce(&ConnectCallback, &success1));
  240. remote_manager_->ConnectToImeEngine(
  241. kValidImeSpec, remote_engine2.BindNewPipeAndPassReceiver(),
  242. test_channel2.CreatePendingRemote(), /*extra=*/{},
  243. base::BindOnce(&ConnectCallback, &success2));
  244. remote_manager_.FlushForTesting();
  245. EXPECT_TRUE(success1);
  246. EXPECT_TRUE(success2);
  247. EXPECT_FALSE(remote_engine1.is_connected());
  248. EXPECT_TRUE(remote_engine2.is_connected());
  249. }
  250. TEST_F(ImeServiceTest,
  251. ConnectToImeEngineCannotConnectIfConnectionFactoryIsConnected) {
  252. bool success1, success2 = true;
  253. MockInputChannel test_channel;
  254. mojo::Remote<mojom::ConnectionFactory> connection_factory;
  255. mojo::Remote<mojom::InputChannel> remote_engine;
  256. remote_manager_->InitializeConnectionFactory(
  257. connection_factory.BindNewPipeAndPassReceiver(),
  258. mojom::ConnectionTarget::kDecoder,
  259. base::BindOnce(&ConnectCallback, &success1));
  260. remote_manager_->ConnectToImeEngine(
  261. kValidImeSpec, remote_engine.BindNewPipeAndPassReceiver(),
  262. test_channel.CreatePendingRemote(), /*extra=*/{},
  263. base::BindOnce(&ConnectCallback, &success2));
  264. remote_manager_.FlushForTesting();
  265. // The second connection should have failed.
  266. EXPECT_TRUE(success1);
  267. EXPECT_FALSE(success2);
  268. EXPECT_TRUE(connection_factory.is_connected());
  269. EXPECT_FALSE(remote_engine.is_connected());
  270. }
  271. TEST_F(ImeServiceTest,
  272. ConnectToImeEngineCanConnectIfConnectionFactoryIsDisconnected) {
  273. bool success1, success2 = true;
  274. MockInputChannel test_channel;
  275. mojo::Remote<mojom::ConnectionFactory> connection_factory;
  276. mojo::Remote<mojom::InputChannel> remote_engine;
  277. remote_manager_->InitializeConnectionFactory(
  278. connection_factory.BindNewPipeAndPassReceiver(),
  279. mojom::ConnectionTarget::kDecoder,
  280. base::BindOnce(&ConnectCallback, &success1));
  281. connection_factory.reset();
  282. remote_manager_->ConnectToImeEngine(
  283. kValidImeSpec, remote_engine.BindNewPipeAndPassReceiver(),
  284. test_channel.CreatePendingRemote(), /*extra=*/{},
  285. base::BindOnce(&ConnectCallback, &success2));
  286. remote_manager_.FlushForTesting();
  287. // The second connection should have succeed since the first connection was
  288. // disconnected.
  289. EXPECT_TRUE(success1);
  290. EXPECT_TRUE(success2);
  291. EXPECT_FALSE(connection_factory.is_bound());
  292. EXPECT_TRUE(remote_engine.is_connected());
  293. }
  294. TEST_F(ImeServiceTest, InitializeConnectionFactoryCanOverrideAnyConnection) {
  295. bool success1, success2, success3 = true;
  296. MockInputChannel test_channel;
  297. mojo::Remote<mojom::ConnectionFactory> connection_factory1,
  298. connection_factory2;
  299. mojo::Remote<mojom::InputChannel> remote_engine;
  300. remote_manager_->ConnectToImeEngine(
  301. kValidImeSpec, remote_engine.BindNewPipeAndPassReceiver(),
  302. test_channel.CreatePendingRemote(), /*extra=*/{},
  303. base::BindOnce(&ConnectCallback, &success1));
  304. remote_manager_->InitializeConnectionFactory(
  305. connection_factory1.BindNewPipeAndPassReceiver(),
  306. mojom::ConnectionTarget::kDecoder,
  307. base::BindOnce(&ConnectCallback, &success2));
  308. remote_manager_->InitializeConnectionFactory(
  309. connection_factory2.BindNewPipeAndPassReceiver(),
  310. mojom::ConnectionTarget::kDecoder,
  311. base::BindOnce(&ConnectCallback, &success3));
  312. remote_manager_.FlushForTesting();
  313. EXPECT_TRUE(success1);
  314. EXPECT_TRUE(success2);
  315. EXPECT_TRUE(success3);
  316. EXPECT_FALSE(remote_engine.is_connected());
  317. EXPECT_FALSE(connection_factory1.is_connected());
  318. EXPECT_TRUE(connection_factory2.is_connected());
  319. }
  320. TEST_F(ImeServiceTest, RuleBasedDoesNotHandleModifierKeys) {
  321. bool success1 = false;
  322. bool success2 = false;
  323. mojo::Remote<mojom::ConnectionFactory> connection_factory;
  324. mojo::PendingAssociatedRemote<mojom::InputMethodHost> host_remote;
  325. mojo::AssociatedRemote<mojom::InputMethod> input_method;
  326. mojo::AssociatedReceiver<mojom::InputMethodHost> host(this);
  327. remote_manager_->InitializeConnectionFactory(
  328. connection_factory.BindNewPipeAndPassReceiver(),
  329. mojom::ConnectionTarget::kImeService,
  330. base::BindOnce(&ConnectCallback, &success1));
  331. remote_manager_.FlushForTesting();
  332. EXPECT_TRUE(success1);
  333. host.Bind(host_remote.InitWithNewEndpointAndPassReceiver());
  334. connection_factory->ConnectToInputMethod(
  335. "m17n:ar", input_method.BindNewEndpointAndPassReceiver(),
  336. std::move(host_remote), nullptr,
  337. base::BindOnce(&ConnectCallback, &success2));
  338. connection_factory.FlushForTesting();
  339. EXPECT_TRUE(success2);
  340. constexpr std::pair<mojom::NamedDomKey, mojom::DomCode> kModifierKeys[] = {
  341. {mojom::NamedDomKey::kShift, mojom::DomCode::kShiftLeft},
  342. {mojom::NamedDomKey::kShift, mojom::DomCode::kShiftRight},
  343. {mojom::NamedDomKey::kAlt, mojom::DomCode::kAltLeft},
  344. {mojom::NamedDomKey::kAlt, mojom::DomCode::kAltRight},
  345. {mojom::NamedDomKey::kAltGraph, mojom::DomCode::kAltRight},
  346. {mojom::NamedDomKey::kCapsLock, mojom::DomCode::kCapsLock},
  347. {mojom::NamedDomKey::kControl, mojom::DomCode::kControlLeft},
  348. {mojom::NamedDomKey::kControl, mojom::DomCode::kControlRight}};
  349. for (const auto& modifier_key : kModifierKeys) {
  350. input_method->ProcessKeyEvent(
  351. mojom::PhysicalKeyEvent::New(
  352. mojom::KeyEventType::kKeyDown,
  353. mojom::DomKey::NewNamedKey(modifier_key.first), modifier_key.second,
  354. mojom::ModifierState::New()),
  355. base::BindLambdaForTesting([](mojom::KeyEventResult result) {
  356. EXPECT_EQ(result, mojom::KeyEventResult::kNeedsHandlingBySystem);
  357. }));
  358. input_method.FlushForTesting();
  359. }
  360. }
  361. TEST_F(ImeServiceTest, RuleBasedDoesNotHandleCtrlShortCut) {
  362. bool success1 = false;
  363. bool success2 = false;
  364. mojo::Remote<mojom::ConnectionFactory> connection_factory;
  365. mojo::PendingAssociatedRemote<mojom::InputMethodHost> host_remote;
  366. mojo::AssociatedRemote<mojom::InputMethod> input_method;
  367. mojo::AssociatedReceiver<mojom::InputMethodHost> host(this);
  368. remote_manager_->InitializeConnectionFactory(
  369. connection_factory.BindNewPipeAndPassReceiver(),
  370. mojom::ConnectionTarget::kImeService,
  371. base::BindOnce(&ConnectCallback, &success1));
  372. remote_manager_.FlushForTesting();
  373. EXPECT_TRUE(success1);
  374. host.Bind(host_remote.InitWithNewEndpointAndPassReceiver());
  375. connection_factory->ConnectToInputMethod(
  376. "m17n:ar", input_method.BindNewEndpointAndPassReceiver(),
  377. std::move(host_remote), nullptr,
  378. base::BindOnce(&ConnectCallback, &success2));
  379. connection_factory.FlushForTesting();
  380. EXPECT_TRUE(success2);
  381. input_method->ProcessKeyEvent(
  382. mojom::PhysicalKeyEvent::New(
  383. mojom::KeyEventType::kKeyDown,
  384. mojom::DomKey::NewNamedKey(mojom::NamedDomKey::kControl),
  385. mojom::DomCode::kControlLeft,
  386. mojom::ModifierState::New()),
  387. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  388. EXPECT_EQ(result, mojom::KeyEventResult::kNeedsHandlingBySystem);
  389. }));
  390. auto modifier_state_with_control = mojom::ModifierState::New();
  391. modifier_state_with_control->control = true;
  392. input_method->ProcessKeyEvent(
  393. mojom::PhysicalKeyEvent::New(
  394. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('a'),
  395. mojom::DomCode::kKeyA, modifier_state_with_control->Clone()),
  396. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  397. EXPECT_EQ(result, mojom::KeyEventResult::kNeedsHandlingBySystem);
  398. }));
  399. input_method.FlushForTesting();
  400. }
  401. TEST_F(ImeServiceTest, RuleBasedDoesNotHandleAltShortCut) {
  402. bool success1 = false;
  403. bool success2 = false;
  404. mojo::Remote<mojom::ConnectionFactory> connection_factory;
  405. mojo::PendingAssociatedRemote<mojom::InputMethodHost> host_remote;
  406. mojo::AssociatedRemote<mojom::InputMethod> input_method;
  407. mojo::AssociatedReceiver<mojom::InputMethodHost> host(this);
  408. remote_manager_->InitializeConnectionFactory(
  409. connection_factory.BindNewPipeAndPassReceiver(),
  410. mojom::ConnectionTarget::kImeService,
  411. base::BindOnce(&ConnectCallback, &success1));
  412. remote_manager_.FlushForTesting();
  413. EXPECT_TRUE(success1);
  414. host.Bind(host_remote.InitWithNewEndpointAndPassReceiver());
  415. connection_factory->ConnectToInputMethod(
  416. "m17n:ar", input_method.BindNewEndpointAndPassReceiver(),
  417. std::move(host_remote), nullptr,
  418. base::BindOnce(&ConnectCallback, &success2));
  419. connection_factory.FlushForTesting();
  420. EXPECT_TRUE(success2);
  421. input_method->ProcessKeyEvent(
  422. mojom::PhysicalKeyEvent::New(
  423. mojom::KeyEventType::kKeyDown,
  424. mojom::DomKey::NewNamedKey(mojom::NamedDomKey::kAlt),
  425. mojom::DomCode::kAltLeft,
  426. mojom::ModifierState::New()),
  427. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  428. EXPECT_EQ(result, mojom::KeyEventResult::kNeedsHandlingBySystem);
  429. }));
  430. auto new_modifier_state = mojom::ModifierState::New();
  431. new_modifier_state->alt = true;
  432. input_method->ProcessKeyEvent(
  433. mojom::PhysicalKeyEvent::New(
  434. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('a'),
  435. mojom::DomCode::kKeyA, std::move(new_modifier_state)),
  436. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  437. EXPECT_EQ(result, mojom::KeyEventResult::kNeedsHandlingBySystem);
  438. }));
  439. input_method.FlushForTesting();
  440. }
  441. TEST_F(ImeServiceTest, RuleBasedHandlesAltRight) {
  442. bool success1 = false;
  443. bool success2 = false;
  444. mojo::Remote<mojom::ConnectionFactory> connection_factory;
  445. mojo::PendingAssociatedRemote<mojom::InputMethodHost> host_remote;
  446. MockInputMethodHost mock_host;
  447. mojo::AssociatedRemote<mojom::InputMethod> input_method;
  448. mojo::AssociatedReceiver<mojom::InputMethodHost> host(&mock_host);
  449. remote_manager_->InitializeConnectionFactory(
  450. connection_factory.BindNewPipeAndPassReceiver(),
  451. mojom::ConnectionTarget::kImeService,
  452. base::BindOnce(&ConnectCallback, &success1));
  453. remote_manager_.FlushForTesting();
  454. EXPECT_TRUE(success1);
  455. host.Bind(host_remote.InitWithNewEndpointAndPassReceiver());
  456. connection_factory->ConnectToInputMethod(
  457. "m17n:ar", input_method.BindNewEndpointAndPassReceiver(),
  458. std::move(host_remote), nullptr,
  459. base::BindOnce(&ConnectCallback, &success2));
  460. connection_factory.FlushForTesting();
  461. EXPECT_TRUE(success2);
  462. input_method->ProcessKeyEvent(
  463. mojom::PhysicalKeyEvent::New(
  464. mojom::KeyEventType::kKeyDown,
  465. mojom::DomKey::NewNamedKey(mojom::NamedDomKey::kAlt),
  466. mojom::DomCode::kAltRight,
  467. mojom::ModifierState::New()),
  468. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  469. EXPECT_EQ(result, mojom::KeyEventResult::kNeedsHandlingBySystem);
  470. }));
  471. auto modifier_state_with_alt = mojom::ModifierState::New();
  472. modifier_state_with_alt->alt = true;
  473. input_method->ProcessKeyEvent(
  474. mojom::PhysicalKeyEvent::New(
  475. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('a'),
  476. mojom::DomCode::kKeyA, modifier_state_with_alt->Clone()),
  477. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  478. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  479. EXPECT_FALSE(mock_host.last_commit.empty());
  480. }));
  481. input_method.FlushForTesting();
  482. }
  483. // Tests that the rule-based Arabic keyboard can work correctly.
  484. TEST_F(ImeServiceTest, RuleBasedArabic) {
  485. bool success1 = false;
  486. bool success2 = false;
  487. mojo::Remote<mojom::ConnectionFactory> connection_factory;
  488. mojo::PendingAssociatedRemote<mojom::InputMethodHost> host_remote;
  489. MockInputMethodHost mock_host;
  490. mojo::AssociatedRemote<mojom::InputMethod> input_method;
  491. mojo::AssociatedReceiver<mojom::InputMethodHost> host(&mock_host);
  492. remote_manager_->InitializeConnectionFactory(
  493. connection_factory.BindNewPipeAndPassReceiver(),
  494. mojom::ConnectionTarget::kImeService,
  495. base::BindOnce(&ConnectCallback, &success1));
  496. remote_manager_.FlushForTesting();
  497. EXPECT_TRUE(success1);
  498. host.Bind(host_remote.InitWithNewEndpointAndPassReceiver());
  499. connection_factory->ConnectToInputMethod(
  500. "m17n:ar", input_method.BindNewEndpointAndPassReceiver(),
  501. std::move(host_remote), nullptr,
  502. base::BindOnce(&ConnectCallback, &success2));
  503. connection_factory.FlushForTesting();
  504. EXPECT_TRUE(success2);
  505. // Test Shift+KeyA.
  506. auto modifier_state_with_shift = mojom::ModifierState::New();
  507. modifier_state_with_shift->shift = true;
  508. input_method->ProcessKeyEvent(
  509. mojom::PhysicalKeyEvent::New(
  510. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('A'),
  511. mojom::DomCode::kKeyA, modifier_state_with_shift->Clone()),
  512. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  513. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  514. EXPECT_EQ(mock_host.last_commit, u"\u0650");
  515. EXPECT_TRUE(mock_host.last_composition.empty());
  516. }));
  517. input_method.FlushForTesting();
  518. // Test KeyB
  519. input_method->ProcessKeyEvent(
  520. mojom::PhysicalKeyEvent::New(
  521. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('b'),
  522. mojom::DomCode::kKeyB, mojom::ModifierState::New()),
  523. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  524. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  525. EXPECT_EQ(mock_host.last_commit, u"\u0644\u0627");
  526. EXPECT_TRUE(mock_host.last_composition.empty());
  527. }));
  528. input_method.FlushForTesting();
  529. // Test unhandled key.
  530. input_method->ProcessKeyEvent(
  531. mojom::PhysicalKeyEvent::New(
  532. mojom::KeyEventType::kKeyDown,
  533. mojom::DomKey::NewNamedKey(mojom::NamedDomKey::kEnter),
  534. mojom::DomCode::kEnter,
  535. mojom::ModifierState::New()),
  536. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  537. EXPECT_EQ(result, mojom::KeyEventResult::kNeedsHandlingBySystem);
  538. }));
  539. input_method.FlushForTesting();
  540. // Test keyup.
  541. input_method->ProcessKeyEvent(
  542. mojom::PhysicalKeyEvent::New(
  543. mojom::KeyEventType::kKeyUp,
  544. mojom::DomKey::NewNamedKey(mojom::NamedDomKey::kEnter),
  545. mojom::DomCode::kEnter,
  546. mojom::ModifierState::New()),
  547. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  548. EXPECT_EQ(result, mojom::KeyEventResult::kNeedsHandlingBySystem);
  549. }));
  550. input_method.FlushForTesting();
  551. // TODO(keithlee) Test reset function
  552. input_method->OnCompositionCanceledBySystem();
  553. }
  554. // Tests that the rule-based DevaPhone keyboard can work correctly.
  555. TEST_F(ImeServiceTest, RuleBasedDevaPhone) {
  556. bool success1 = false;
  557. bool success2 = false;
  558. mojo::Remote<mojom::ConnectionFactory> connection_factory;
  559. mojo::PendingAssociatedRemote<mojom::InputMethodHost> host_remote;
  560. MockInputMethodHost mock_host;
  561. mojo::AssociatedRemote<mojom::InputMethod> input_method;
  562. mojo::AssociatedReceiver<mojom::InputMethodHost> host(&mock_host);
  563. remote_manager_->InitializeConnectionFactory(
  564. connection_factory.BindNewPipeAndPassReceiver(),
  565. mojom::ConnectionTarget::kImeService,
  566. base::BindOnce(&ConnectCallback, &success1));
  567. remote_manager_.FlushForTesting();
  568. EXPECT_TRUE(success1);
  569. host.Bind(host_remote.InitWithNewEndpointAndPassReceiver());
  570. connection_factory->ConnectToInputMethod(
  571. "m17n:deva_phone", input_method.BindNewEndpointAndPassReceiver(),
  572. std::move(host_remote), nullptr,
  573. base::BindOnce(&ConnectCallback, &success2));
  574. connection_factory.FlushForTesting();
  575. EXPECT_TRUE(success2);
  576. // Test KeyN.
  577. input_method->ProcessKeyEvent(
  578. mojom::PhysicalKeyEvent::New(
  579. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('n'),
  580. mojom::DomCode::kKeyN, mojom::ModifierState::New()),
  581. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  582. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  583. EXPECT_TRUE(mock_host.last_commit.empty());
  584. EXPECT_EQ(mock_host.last_composition, u"\u0928");
  585. }));
  586. input_method.FlushForTesting();
  587. // Backspace.
  588. input_method->ProcessKeyEvent(
  589. mojom::PhysicalKeyEvent::New(
  590. mojom::KeyEventType::kKeyDown,
  591. mojom::DomKey::NewNamedKey(mojom::NamedDomKey::kBackspace),
  592. mojom::DomCode::kBackspace,
  593. mojom::ModifierState::New()),
  594. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  595. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  596. EXPECT_TRUE(mock_host.last_commit.empty());
  597. EXPECT_EQ(mock_host.last_composition, u"");
  598. }));
  599. input_method.FlushForTesting();
  600. // KeyN + KeyC.
  601. input_method->ProcessKeyEvent(
  602. mojom::PhysicalKeyEvent::New(
  603. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('n'),
  604. mojom::DomCode::kKeyN, mojom::ModifierState::New()),
  605. base::DoNothing());
  606. input_method->ProcessKeyEvent(
  607. mojom::PhysicalKeyEvent::New(
  608. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('c'),
  609. mojom::DomCode::kKeyC, mojom::ModifierState::New()),
  610. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  611. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  612. EXPECT_EQ(mock_host.last_composition, u"\u091e\u094d\u091a");
  613. }));
  614. input_method.FlushForTesting();
  615. // Space.
  616. input_method->ProcessKeyEvent(
  617. mojom::PhysicalKeyEvent::New(
  618. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint(' '),
  619. mojom::DomCode::kSpace, mojom::ModifierState::New()),
  620. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  621. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  622. EXPECT_EQ(mock_host.last_composition, u"\u091e\u094d\u091a");
  623. }));
  624. input_method.FlushForTesting();
  625. }
  626. // Tests escapable characters. See https://crbug.com/1014384.
  627. TEST_F(ImeServiceTest, RuleBasedDoesNotEscapeCharacters) {
  628. bool success1 = false;
  629. bool success2 = false;
  630. mojo::Remote<mojom::ConnectionFactory> connection_factory;
  631. mojo::PendingAssociatedRemote<mojom::InputMethodHost> host_remote;
  632. MockInputMethodHost mock_host;
  633. mojo::AssociatedRemote<mojom::InputMethod> input_method;
  634. mojo::AssociatedReceiver<mojom::InputMethodHost> host(&mock_host);
  635. remote_manager_->InitializeConnectionFactory(
  636. connection_factory.BindNewPipeAndPassReceiver(),
  637. mojom::ConnectionTarget::kImeService,
  638. base::BindOnce(&ConnectCallback, &success1));
  639. remote_manager_.FlushForTesting();
  640. EXPECT_TRUE(success1);
  641. host.Bind(host_remote.InitWithNewEndpointAndPassReceiver());
  642. connection_factory->ConnectToInputMethod(
  643. "m17n:deva_phone", input_method.BindNewEndpointAndPassReceiver(),
  644. std::move(host_remote), nullptr,
  645. base::BindOnce(&ConnectCallback, &success2));
  646. connection_factory.FlushForTesting();
  647. EXPECT_TRUE(success2);
  648. auto modifier_state_with_shift = mojom::ModifierState::New();
  649. modifier_state_with_shift->shift = true;
  650. // Test Shift+Quote ('"').
  651. input_method->ProcessKeyEvent(
  652. mojom::PhysicalKeyEvent::New(
  653. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('"'),
  654. mojom::DomCode::kQuote, modifier_state_with_shift->Clone()),
  655. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  656. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  657. EXPECT_EQ(mock_host.last_commit, u"\"");
  658. EXPECT_TRUE(mock_host.last_composition.empty());
  659. }));
  660. input_method.FlushForTesting();
  661. // Backslash.
  662. input_method->ProcessKeyEvent(
  663. mojom::PhysicalKeyEvent::New(
  664. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('\\'),
  665. mojom::DomCode::kBackslash, mojom::ModifierState::New()),
  666. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  667. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  668. EXPECT_EQ(mock_host.last_commit, u"\\");
  669. EXPECT_TRUE(mock_host.last_composition.empty());
  670. }));
  671. input_method.FlushForTesting();
  672. // Shift+Comma ('<')
  673. input_method->ProcessKeyEvent(
  674. mojom::PhysicalKeyEvent::New(
  675. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('<'),
  676. mojom::DomCode::kComma, modifier_state_with_shift->Clone()),
  677. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  678. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  679. EXPECT_EQ(mock_host.last_commit, u"<");
  680. EXPECT_TRUE(mock_host.last_composition.empty());
  681. }));
  682. input_method.FlushForTesting();
  683. }
  684. // Tests that AltGr works with rule-based. See crbug.com/1035145.
  685. TEST_F(ImeServiceTest, KhmerKeyboardAltGr) {
  686. bool success1 = false;
  687. bool success2 = false;
  688. mojo::Remote<mojom::ConnectionFactory> connection_factory;
  689. mojo::PendingAssociatedRemote<mojom::InputMethodHost> host_remote;
  690. MockInputMethodHost mock_host;
  691. mojo::AssociatedRemote<mojom::InputMethod> input_method;
  692. mojo::AssociatedReceiver<mojom::InputMethodHost> host(&mock_host);
  693. remote_manager_->InitializeConnectionFactory(
  694. connection_factory.BindNewPipeAndPassReceiver(),
  695. mojom::ConnectionTarget::kImeService,
  696. base::BindOnce(&ConnectCallback, &success1));
  697. remote_manager_.FlushForTesting();
  698. EXPECT_TRUE(success1);
  699. host.Bind(host_remote.InitWithNewEndpointAndPassReceiver());
  700. connection_factory->ConnectToInputMethod(
  701. "m17n:km", input_method.BindNewEndpointAndPassReceiver(),
  702. std::move(host_remote), nullptr,
  703. base::BindOnce(&ConnectCallback, &success2));
  704. connection_factory.FlushForTesting();
  705. EXPECT_TRUE(success2);
  706. // Test AltRight+KeyA.
  707. // We do not support AltGr for rule-based. We treat AltRight as AltGr.
  708. input_method->ProcessKeyEvent(
  709. mojom::PhysicalKeyEvent::New(
  710. mojom::KeyEventType::kKeyDown,
  711. mojom::DomKey::NewNamedKey(mojom::NamedDomKey::kAlt),
  712. mojom::DomCode::kAltRight,
  713. mojom::ModifierState::New()),
  714. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  715. EXPECT_EQ(result, mojom::KeyEventResult::kNeedsHandlingBySystem);
  716. }));
  717. auto modifier_state_with_alt = mojom::ModifierState::New();
  718. modifier_state_with_alt->alt = true;
  719. input_method->ProcessKeyEvent(
  720. mojom::PhysicalKeyEvent::New(
  721. mojom::KeyEventType::kKeyDown, mojom::DomKey::NewCodepoint('a'),
  722. mojom::DomCode::kKeyA, modifier_state_with_alt->Clone()),
  723. base::BindLambdaForTesting([&](mojom::KeyEventResult result) {
  724. EXPECT_EQ(result, mojom::KeyEventResult::kConsumedByIme);
  725. EXPECT_EQ(mock_host.last_commit, u"+");
  726. EXPECT_TRUE(mock_host.last_composition.empty());
  727. }));
  728. input_method.FlushForTesting();
  729. }
  730. TEST_F(ImeServiceTest, GetFieldTrialParamValueByFeatureNonConsidered) {
  731. const char* value = service_->GetFieldTrialParamValueByFeature(
  732. "non-considered-feature", "param-name");
  733. EXPECT_STREQ(value, "");
  734. delete[] value;
  735. }
  736. TEST_F(ImeServiceTest, GetFieldTrialParamValueByFeatureConsidered) {
  737. const char* value = service_->GetFieldTrialParamValueByFeature(
  738. "AutocorrectParamsTuning", "param-name");
  739. EXPECT_STREQ(value, "AutocorrectParamsTuning::param-name");
  740. delete[] value;
  741. }
  742. } // namespace ime
  743. } // namespace ash