test_ime_input_event.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. // Copyright (c) 2012 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 "ppapi/tests/test_ime_input_event.h"
  5. #include "ppapi/c/pp_errors.h"
  6. #include "ppapi/c/ppb_input_event.h"
  7. #include "ppapi/cpp/input_event.h"
  8. #include "ppapi/cpp/module.h"
  9. #include "ppapi/tests/test_utils.h"
  10. #include "ppapi/tests/testing_instance.h"
  11. REGISTER_TEST_CASE(ImeInputEvent);
  12. namespace {
  13. // Japanese Kanji letters
  14. const char* kCompositionChar[] = {
  15. "\xE6\x96\x87", // An example character of normal unicode.
  16. "\xF0\xA0\xAE\x9F", // An example character of surrogate pair.
  17. "\xF0\x9F\x98\x81" // An example character of surrogate pair(emoji).
  18. };
  19. const char kCompositionText[] = "\xE6\x96\x87\xF0\xA0\xAE\x9F\xF0\x9F\x98\x81";
  20. #define FINISHED_WAITING_MESSAGE "TEST_IME_INPUT_EVENT_FINISHED_WAITING"
  21. } // namespace
  22. TestImeInputEvent::TestImeInputEvent(TestingInstance* instance)
  23. : TestCase(instance),
  24. input_event_interface_(NULL),
  25. keyboard_input_event_interface_(NULL),
  26. ime_input_event_interface_(NULL),
  27. received_unexpected_event_(true),
  28. received_finish_message_(false) {
  29. }
  30. TestImeInputEvent::~TestImeInputEvent() {
  31. // Remove the special listener that only responds to a
  32. // FINISHED_WAITING_MESSAGE string. See Init for where it gets added.
  33. std::string js_code;
  34. js_code = "var plugin = document.getElementById('plugin');"
  35. "plugin.removeEventListener('message',"
  36. " plugin.wait_for_messages_handler);"
  37. "delete plugin.wait_for_messages_handler;";
  38. instance_->EvalScript(js_code);
  39. }
  40. void TestImeInputEvent::RunTests(const std::string& filter) {
  41. RUN_TEST(ImeCommit, filter);
  42. RUN_TEST(ImeCancel, filter);
  43. RUN_TEST(ImeUnawareCommit, filter);
  44. RUN_TEST(ImeUnawareCancel, filter);
  45. }
  46. bool TestImeInputEvent::Init() {
  47. input_event_interface_ = static_cast<const PPB_InputEvent*>(
  48. pp::Module::Get()->GetBrowserInterface(PPB_INPUT_EVENT_INTERFACE));
  49. keyboard_input_event_interface_ =
  50. static_cast<const PPB_KeyboardInputEvent*>(
  51. pp::Module::Get()->GetBrowserInterface(
  52. PPB_KEYBOARD_INPUT_EVENT_INTERFACE));
  53. ime_input_event_interface_ = static_cast<const PPB_IMEInputEvent*>(
  54. pp::Module::Get()->GetBrowserInterface(
  55. PPB_IME_INPUT_EVENT_INTERFACE));
  56. bool success =
  57. input_event_interface_ &&
  58. keyboard_input_event_interface_ &&
  59. ime_input_event_interface_ &&
  60. CheckTestingInterface();
  61. // Set up a listener for our message that signals that all input events have
  62. // been received.
  63. // Note the following code is dependent on some features of test_case.html.
  64. // E.g., it is assumed that the DOM element where the plugin is embedded has
  65. // an id of 'plugin', and there is a function 'IsTestingMessage' that allows
  66. // us to ignore the messages that are intended for use by the testing
  67. // framework itself.
  68. std::string js_code =
  69. "var plugin = document.getElementById('plugin');"
  70. "var wait_for_messages_handler = function(message_event) {"
  71. " if (!IsTestingMessage(message_event.data) &&"
  72. " message_event.data === '" FINISHED_WAITING_MESSAGE "') {"
  73. " plugin.postMessage('" FINISHED_WAITING_MESSAGE "');"
  74. " }"
  75. "};"
  76. "plugin.addEventListener('message', wait_for_messages_handler);"
  77. // Stash it on the plugin so we can remove it in the destructor.
  78. "plugin.wait_for_messages_handler = wait_for_messages_handler;";
  79. instance_->EvalScript(js_code);
  80. return success;
  81. }
  82. bool TestImeInputEvent::HandleInputEvent(const pp::InputEvent& input_event) {
  83. // Check whether the IME related events comes in the expected order.
  84. switch (input_event.GetType()) {
  85. case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START:
  86. case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE:
  87. case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END:
  88. case PP_INPUTEVENT_TYPE_IME_TEXT:
  89. case PP_INPUTEVENT_TYPE_CHAR:
  90. if (expected_events_.empty()) {
  91. received_unexpected_event_ = true;
  92. } else {
  93. received_unexpected_event_ =
  94. !AreEquivalentEvents(input_event.pp_resource(),
  95. expected_events_.front().pp_resource());
  96. expected_events_.erase(expected_events_.begin());
  97. }
  98. break;
  99. default:
  100. // Don't care for any other input event types for this test.
  101. break;
  102. }
  103. // Handle all input events.
  104. return true;
  105. }
  106. void TestImeInputEvent::HandleMessage(const pp::Var& message_data) {
  107. if (message_data.is_string() &&
  108. (message_data.AsString() == FINISHED_WAITING_MESSAGE)) {
  109. testing_interface_->QuitMessageLoop(instance_->pp_instance());
  110. received_finish_message_ = true;
  111. }
  112. }
  113. void TestImeInputEvent::DidChangeView(const pp::View& view) {
  114. view_rect_ = view.GetRect();
  115. }
  116. pp::InputEvent TestImeInputEvent::CreateImeCompositionStartEvent() {
  117. return pp::IMEInputEvent(
  118. instance_,
  119. PP_INPUTEVENT_TYPE_IME_COMPOSITION_START,
  120. 100, // time_stamp
  121. pp::Var(""),
  122. std::vector<uint32_t>(),
  123. -1, // target_segment
  124. std::make_pair(0U, 0U) // selection
  125. );
  126. }
  127. pp::InputEvent TestImeInputEvent::CreateImeCompositionUpdateEvent(
  128. const std::string& text,
  129. const std::vector<uint32_t>& segments,
  130. int32_t target_segment,
  131. const std::pair<uint32_t, uint32_t>& selection) {
  132. return pp::IMEInputEvent(
  133. instance_,
  134. PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE,
  135. 100, // time_stamp
  136. text,
  137. segments,
  138. target_segment,
  139. selection
  140. );
  141. }
  142. pp::InputEvent TestImeInputEvent::CreateImeCompositionEndEvent(
  143. const std::string& text) {
  144. return pp::IMEInputEvent(
  145. instance_,
  146. PP_INPUTEVENT_TYPE_IME_COMPOSITION_END,
  147. 100, // time_stamp
  148. pp::Var(text),
  149. std::vector<uint32_t>(),
  150. -1, // target_segment
  151. std::make_pair(0U, 0U) // selection
  152. );
  153. }
  154. pp::InputEvent TestImeInputEvent::CreateImeTextEvent(const std::string& text) {
  155. return pp::IMEInputEvent(
  156. instance_,
  157. PP_INPUTEVENT_TYPE_IME_TEXT,
  158. 100, // time_stamp
  159. pp::Var(text),
  160. std::vector<uint32_t>(),
  161. -1, // target_segment
  162. std::make_pair(0U, 0U) // selection
  163. );
  164. }
  165. pp::InputEvent TestImeInputEvent::CreateCharEvent(const std::string& text) {
  166. return pp::KeyboardInputEvent(
  167. instance_,
  168. PP_INPUTEVENT_TYPE_CHAR,
  169. 100, // time_stamp
  170. 0, // modifiers
  171. 0, // keycode
  172. pp::Var(text),
  173. pp::Var());
  174. }
  175. void TestImeInputEvent::GetFocusBySimulatingMouseClick() {
  176. // For receiving IME events, the plugin DOM node needs to be focused.
  177. // The following code is for achieving that by simulating a mouse click event.
  178. input_event_interface_->RequestInputEvents(instance_->pp_instance(),
  179. PP_INPUTEVENT_CLASS_MOUSE);
  180. SimulateInputEvent(pp::MouseInputEvent(
  181. instance_,
  182. PP_INPUTEVENT_TYPE_MOUSEDOWN,
  183. 100, // time_stamp
  184. 0, // modifiers
  185. PP_INPUTEVENT_MOUSEBUTTON_LEFT,
  186. pp::Point(
  187. view_rect_.x() + view_rect_.width() / 2,
  188. view_rect_.y() + view_rect_.height() / 2),
  189. 1, // click count
  190. pp::Point())); // movement
  191. }
  192. // Simulates the input event and calls PostMessage to let us know when
  193. // we have received all resulting events from the browser.
  194. bool TestImeInputEvent::SimulateInputEvent(const pp::InputEvent& input_event) {
  195. received_unexpected_event_ = false;
  196. received_finish_message_ = false;
  197. testing_interface_->SimulateInputEvent(instance_->pp_instance(),
  198. input_event.pp_resource());
  199. instance_->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE));
  200. testing_interface_->RunMessageLoop(instance_->pp_instance());
  201. return received_finish_message_ && !received_unexpected_event_;
  202. }
  203. bool TestImeInputEvent::AreEquivalentEvents(PP_Resource received,
  204. PP_Resource expected) {
  205. if (!input_event_interface_->IsInputEvent(received) ||
  206. !input_event_interface_->IsInputEvent(expected)) {
  207. return false;
  208. }
  209. // Test common fields, except modifiers and time stamp, which may be changed
  210. // by the browser.
  211. int32_t received_type = input_event_interface_->GetType(received);
  212. int32_t expected_type = input_event_interface_->GetType(expected);
  213. if (received_type != expected_type)
  214. return false;
  215. // Test event type-specific fields.
  216. switch (received_type) {
  217. case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START:
  218. // COMPOSITION_START does not convey further information.
  219. break;
  220. case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END:
  221. case PP_INPUTEVENT_TYPE_IME_TEXT:
  222. // For COMPOSITION_END and TEXT, GetText() has meaning.
  223. return pp::Var(pp::PASS_REF,
  224. ime_input_event_interface_->GetText(received)) ==
  225. pp::Var(pp::PASS_REF,
  226. ime_input_event_interface_->GetText(expected));
  227. case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE:
  228. // For COMPOSITION_UPDATE, all fields must be checked.
  229. {
  230. uint32_t received_segment_number =
  231. ime_input_event_interface_->GetSegmentNumber(received);
  232. uint32_t expected_segment_number =
  233. ime_input_event_interface_->GetSegmentNumber(expected);
  234. if (received_segment_number != expected_segment_number)
  235. return false;
  236. // The "<=" is not a bug. i-th segment is represented as the pair of
  237. // i-th and (i+1)-th offsets in Pepper IME API.
  238. for (uint32_t i = 0; i <= received_segment_number; ++i) {
  239. if (ime_input_event_interface_->GetSegmentOffset(received, i) !=
  240. ime_input_event_interface_->GetSegmentOffset(expected, i))
  241. return false;
  242. }
  243. uint32_t received_selection_start = 0;
  244. uint32_t received_selection_end = 0;
  245. uint32_t expected_selection_start = 0;
  246. uint32_t expected_selection_end = 0;
  247. ime_input_event_interface_->GetSelection(
  248. received, &received_selection_start, &received_selection_end);
  249. ime_input_event_interface_->GetSelection(
  250. expected, &expected_selection_start, &expected_selection_end);
  251. if (received_selection_start != expected_selection_start ||
  252. received_selection_end != expected_selection_end) {
  253. return true;
  254. }
  255. return pp::Var(pp::PASS_REF,
  256. ime_input_event_interface_->GetText(received)) ==
  257. pp::Var(pp::PASS_REF,
  258. ime_input_event_interface_->GetText(expected)) &&
  259. ime_input_event_interface_->GetTargetSegment(received) ==
  260. ime_input_event_interface_->GetTargetSegment(expected);
  261. }
  262. case PP_INPUTEVENT_TYPE_CHAR:
  263. return
  264. keyboard_input_event_interface_->GetKeyCode(received) ==
  265. keyboard_input_event_interface_->GetKeyCode(expected) &&
  266. pp::Var(pp::PASS_REF,
  267. keyboard_input_event_interface_->GetCharacterText(received)) ==
  268. pp::Var(pp::PASS_REF,
  269. keyboard_input_event_interface_->GetCharacterText(expected));
  270. default:
  271. break;
  272. }
  273. return true;
  274. }
  275. std::string TestImeInputEvent::TestImeCommit() {
  276. GetFocusBySimulatingMouseClick();
  277. input_event_interface_->RequestInputEvents(instance_->pp_instance(),
  278. PP_INPUTEVENT_CLASS_KEYBOARD |
  279. PP_INPUTEVENT_CLASS_IME);
  280. std::vector<uint32_t> segments;
  281. segments.push_back(0U);
  282. segments.push_back(3U);
  283. segments.push_back(7U);
  284. segments.push_back(11U);
  285. pp::InputEvent update_event = CreateImeCompositionUpdateEvent(
  286. kCompositionText, segments, 1, std::make_pair(3U, 7U));
  287. expected_events_.clear();
  288. expected_events_.push_back(CreateImeCompositionStartEvent());
  289. expected_events_.push_back(update_event);
  290. expected_events_.push_back(CreateImeCompositionEndEvent(kCompositionText));
  291. expected_events_.push_back(CreateImeTextEvent(kCompositionText));
  292. // Simulate the case when IME successfully committed some text.
  293. ASSERT_TRUE(SimulateInputEvent(update_event));
  294. ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText)));
  295. ASSERT_TRUE(expected_events_.empty());
  296. PASS();
  297. }
  298. std::string TestImeInputEvent::TestImeCancel() {
  299. GetFocusBySimulatingMouseClick();
  300. input_event_interface_->RequestInputEvents(instance_->pp_instance(),
  301. PP_INPUTEVENT_CLASS_KEYBOARD |
  302. PP_INPUTEVENT_CLASS_IME);
  303. std::vector<uint32_t> segments;
  304. segments.push_back(0U);
  305. segments.push_back(3U);
  306. segments.push_back(7U);
  307. segments.push_back(11U);
  308. pp::InputEvent update_event = CreateImeCompositionUpdateEvent(
  309. kCompositionText, segments, 1, std::make_pair(3U, 7U));
  310. expected_events_.clear();
  311. expected_events_.push_back(CreateImeCompositionStartEvent());
  312. expected_events_.push_back(update_event);
  313. expected_events_.push_back(CreateImeCompositionEndEvent(std::string()));
  314. // Simulate the case when IME canceled composition.
  315. ASSERT_TRUE(SimulateInputEvent(update_event));
  316. ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(std::string())));
  317. ASSERT_TRUE(expected_events_.empty());
  318. PASS();
  319. }
  320. std::string TestImeInputEvent::TestImeUnawareCommit() {
  321. GetFocusBySimulatingMouseClick();
  322. input_event_interface_->ClearInputEventRequest(instance_->pp_instance(),
  323. PP_INPUTEVENT_CLASS_IME);
  324. input_event_interface_->RequestInputEvents(instance_->pp_instance(),
  325. PP_INPUTEVENT_CLASS_KEYBOARD);
  326. std::vector<uint32_t> segments;
  327. segments.push_back(0U);
  328. segments.push_back(3U);
  329. segments.push_back(7U);
  330. segments.push_back(11U);
  331. pp::InputEvent update_event = CreateImeCompositionUpdateEvent(
  332. kCompositionText, segments, 1, std::make_pair(3U, 7U));
  333. expected_events_.clear();
  334. expected_events_.push_back(CreateCharEvent(kCompositionChar[0]));
  335. expected_events_.push_back(CreateCharEvent(kCompositionChar[1]));
  336. expected_events_.push_back(CreateCharEvent(kCompositionChar[2]));
  337. // Test for IME-unaware plugins. Commit event is translated to char events.
  338. ASSERT_TRUE(SimulateInputEvent(update_event));
  339. ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText)));
  340. ASSERT_TRUE(expected_events_.empty());
  341. PASS();
  342. }
  343. std::string TestImeInputEvent::TestImeUnawareCancel() {
  344. GetFocusBySimulatingMouseClick();
  345. input_event_interface_->ClearInputEventRequest(instance_->pp_instance(),
  346. PP_INPUTEVENT_CLASS_IME);
  347. input_event_interface_->RequestInputEvents(instance_->pp_instance(),
  348. PP_INPUTEVENT_CLASS_KEYBOARD);
  349. std::vector<uint32_t> segments;
  350. segments.push_back(0U);
  351. segments.push_back(3U);
  352. segments.push_back(7U);
  353. segments.push_back(11U);
  354. pp::InputEvent update_event = CreateImeCompositionUpdateEvent(
  355. kCompositionText, segments, 1, std::make_pair(3U, 7U));
  356. expected_events_.clear();
  357. // Test for IME-unaware plugins. Cancel won't issue any events.
  358. ASSERT_TRUE(SimulateInputEvent(update_event));
  359. ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(std::string())));
  360. ASSERT_TRUE(expected_events_.empty());
  361. PASS();
  362. }