Index: ppapi/tests/test_ime_input_event.cc |
diff --git a/ppapi/tests/test_ime_input_event.cc b/ppapi/tests/test_ime_input_event.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..804e17d7abed7a640ba72fba80e325e3e212f4cb |
--- /dev/null |
+++ b/ppapi/tests/test_ime_input_event.cc |
@@ -0,0 +1,425 @@ |
+// Copyright (c) 2012 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 "ppapi/tests/test_ime_input_event.h" |
+ |
+#include "ppapi/c/dev/ppb_ime_input_event_dev.h" |
+#include "ppapi/c/dev/ppb_testing_dev.h" |
+#include "ppapi/c/pp_errors.h" |
+#include "ppapi/c/ppb_input_event.h" |
+#include "ppapi/cpp/dev/ime_input_event_dev.h" |
+#include "ppapi/cpp/input_event.h" |
+#include "ppapi/cpp/module.h" |
+#include "ppapi/tests/test_utils.h" |
+#include "ppapi/tests/testing_instance.h" |
+ |
+REGISTER_TEST_CASE(ImeInputEvent); |
+ |
+namespace { |
+ |
+// Japanese Kanji letters meaning "a string" ('mo' 'ji' 'retsu' in Kanji) |
+const char* kCompositionChar[] = { |
+ "\xE6\x96\x87", "\xE5\xAD\x97", "\xE5\x88\x97" |
+}; |
+ |
+const char kCompositionText[] = "\xE6\x96\x87\xE5\xAD\x97\xE5\x88\x97"; |
+ |
+#define FINISHED_WAITING_MESSAGE "TEST_IME_INPUT_EVENT_FINISHED_WAITING" |
+ |
+} // namespace |
+ |
+TestImeInputEvent::TestImeInputEvent(TestingInstance* instance) |
+ : TestCase(instance), |
+ input_event_interface_(NULL), |
+ keyboard_input_event_interface_(NULL), |
+ ime_input_event_interface_(NULL), |
+ received_unexpected_event_(true), |
+ received_finish_message_(false) { |
+} |
+ |
+TestImeInputEvent::~TestImeInputEvent() { |
+ // Remove the special listener that only responds to a |
+ // FINISHED_WAITING_MESSAGE string. See Init for where it gets added. |
+ std::string js_code; |
+ js_code = "var plugin = document.getElementById('plugin');" |
+ "plugin.removeEventListener('message'," |
+ " plugin.wait_for_messages_handler);" |
+ "delete plugin.wait_for_messages_handler;"; |
+ instance_->EvalScript(js_code); |
+} |
+ |
+void TestImeInputEvent::RunTests(const std::string& filter) { |
+ RUN_TEST(ImeCommit, filter); |
+ RUN_TEST(ImeCancel, filter); |
+ RUN_TEST(ImeUnawareCommit, filter); |
+ RUN_TEST(ImeUnawareCancel, filter); |
+} |
+ |
+bool TestImeInputEvent::Init() { |
+ input_event_interface_ = static_cast<const PPB_InputEvent*>( |
+ pp::Module::Get()->GetBrowserInterface(PPB_INPUT_EVENT_INTERFACE)); |
+ keyboard_input_event_interface_ = |
+ static_cast<const PPB_KeyboardInputEvent*>( |
+ pp::Module::Get()->GetBrowserInterface( |
+ PPB_KEYBOARD_INPUT_EVENT_INTERFACE)); |
+ ime_input_event_interface_ = static_cast<const PPB_IMEInputEvent_Dev*>( |
+ pp::Module::Get()->GetBrowserInterface( |
+ PPB_IME_INPUT_EVENT_DEV_INTERFACE)); |
+ |
+ bool success = |
+ input_event_interface_ && |
+ keyboard_input_event_interface_ && |
+ ime_input_event_interface_ && |
+ CheckTestingInterface(); |
+ |
+ // Set up a listener for our message that signals that all input events have |
+ // been received. |
+ // Note the following code is dependent on some features of test_case.html. |
+ // E.g., it is assumed that the DOM element where the plugin is embedded has |
+ // an id of 'plugin', and there is a function 'IsTestingMessage' that allows |
+ // us to ignore the messages that are intended for use by the testing |
+ // framework itself. |
+ std::string js_code = |
+ "var plugin = document.getElementById('plugin');" |
+ "var wait_for_messages_handler = function(message_event) {" |
+ " if (!IsTestingMessage(message_event.data) &&" |
+ " message_event.data === '" FINISHED_WAITING_MESSAGE "') {" |
+ " plugin.postMessage('" FINISHED_WAITING_MESSAGE "');" |
+ " }" |
+ "};" |
+ "plugin.addEventListener('message', wait_for_messages_handler);" |
+ // Stash it on the plugin so we can remove it in the destructor. |
+ "plugin.wait_for_messages_handler = wait_for_messages_handler;"; |
+ instance_->EvalScript(js_code); |
+ |
+ return success; |
+} |
+ |
+bool TestImeInputEvent::HandleInputEvent(const pp::InputEvent& input_event) { |
+ // Check whether the IME related events comes in the expected order. |
+ switch (input_event.GetType()) { |
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: |
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: |
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: |
+ case PP_INPUTEVENT_TYPE_IME_TEXT: |
+ case PP_INPUTEVENT_TYPE_CHAR: |
+ if (expected_events_.empty()) { |
+ received_unexpected_event_ = true; |
+ } else { |
+ received_unexpected_event_ = |
+ !AreEquivalentEvents(input_event.pp_resource(), |
+ expected_events_.front().pp_resource()); |
+ expected_events_.erase(expected_events_.begin()); |
+ } |
+ break; |
+ |
+ default: |
+ // Don't care for any other input event types for this test. |
+ break; |
+ } |
+ |
+ // Handle all input events. |
+ return true; |
+} |
+ |
+void TestImeInputEvent::HandleMessage(const pp::Var& message_data) { |
+ if (message_data.is_string() && |
+ (message_data.AsString() == FINISHED_WAITING_MESSAGE)) { |
+ testing_interface_->QuitMessageLoop(instance_->pp_instance()); |
+ received_finish_message_ = true; |
+ } |
+} |
+ |
+void TestImeInputEvent::DidChangeView(const pp::View& view) { |
+ view_rect_ = view.GetRect(); |
+} |
+ |
+pp::InputEvent TestImeInputEvent::CreateImeCompositionStartEvent() { |
+ return pp::IMEInputEvent_Dev( |
+ instance_, |
+ PP_INPUTEVENT_TYPE_IME_COMPOSITION_START, |
+ 100, // time_stamp |
+ pp::Var(""), |
+ std::vector<uint32_t>(), |
+ -1, // target_segment |
+ std::make_pair(0U, 0U) // selection |
+ ); |
+} |
+ |
+pp::InputEvent TestImeInputEvent::CreateImeCompositionUpdateEvent( |
+ const std::string& text, |
+ const std::vector<uint32_t>& segments, |
+ int32_t target_segment, |
+ const std::pair<uint32_t, uint32_t>& selection) { |
+ return pp::IMEInputEvent_Dev( |
+ instance_, |
+ PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE, |
+ 100, // time_stamp |
+ text, |
+ segments, |
+ target_segment, |
+ selection |
+ ); |
+} |
+ |
+pp::InputEvent TestImeInputEvent::CreateImeCompositionEndEvent( |
+ const std::string& text) { |
+ return pp::IMEInputEvent_Dev( |
+ instance_, |
+ PP_INPUTEVENT_TYPE_IME_COMPOSITION_END, |
+ 100, // time_stamp |
+ pp::Var(text), |
+ std::vector<uint32_t>(), |
+ -1, // target_segment |
+ std::make_pair(0U, 0U) // selection |
+ ); |
+} |
+ |
+pp::InputEvent TestImeInputEvent::CreateImeTextEvent(const std::string& text) { |
+ return pp::IMEInputEvent_Dev( |
+ instance_, |
+ PP_INPUTEVENT_TYPE_IME_TEXT, |
+ 100, // time_stamp |
+ pp::Var(text), |
+ std::vector<uint32_t>(), |
+ -1, // target_segment |
+ std::make_pair(0U, 0U) // selection |
+ ); |
+} |
+ |
+pp::InputEvent TestImeInputEvent::CreateCharEvent(const std::string& text) { |
+ return pp::KeyboardInputEvent( |
+ instance_, |
+ PP_INPUTEVENT_TYPE_CHAR, |
+ 100, // time_stamp |
+ 0, // modifiers |
+ 0, // keycode |
+ pp::Var(text)); |
+} |
+ |
+void TestImeInputEvent::GetFocusBySimulatingMouseClick() { |
+ // For receiving IME events, the plugin DOM node needs to be focused. |
+ // The following code is for achieving that by simulating a mouse click event. |
+ input_event_interface_->RequestInputEvents(instance_->pp_instance(), |
+ PP_INPUTEVENT_CLASS_MOUSE); |
+ SimulateInputEvent(pp::MouseInputEvent( |
+ instance_, |
+ PP_INPUTEVENT_TYPE_MOUSEDOWN, |
+ 100, // time_stamp |
+ 0, // modifiers |
+ PP_INPUTEVENT_MOUSEBUTTON_LEFT, |
+ pp::Point( |
+ view_rect_.x() + view_rect_.width() / 2, |
+ view_rect_.y() + view_rect_.height() / 2), |
+ 1, // click count |
+ pp::Point())); // movement |
+} |
+ |
+// Simulates the input event and calls PostMessage to let us know when |
+// we have received all resulting events from the browser. |
+bool TestImeInputEvent::SimulateInputEvent(const pp::InputEvent& input_event) { |
+ received_unexpected_event_ = false; |
+ received_finish_message_ = false; |
+ testing_interface_->SimulateInputEvent(instance_->pp_instance(), |
+ input_event.pp_resource()); |
+ instance_->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE)); |
+ testing_interface_->RunMessageLoop(instance_->pp_instance()); |
+ return received_finish_message_ && !received_unexpected_event_; |
+} |
+ |
+bool TestImeInputEvent::AreEquivalentEvents(PP_Resource received, |
+ PP_Resource expected) { |
+ if (!input_event_interface_->IsInputEvent(received) || |
+ !input_event_interface_->IsInputEvent(expected)) { |
+ return false; |
+ } |
+ |
+ // Test common fields, except modifiers and time stamp, which may be changed |
+ // by the browser. |
+ int32_t received_type = input_event_interface_->GetType(received); |
+ int32_t expected_type = input_event_interface_->GetType(expected); |
+ if (received_type != expected_type) |
+ return false; |
+ |
+ // Test event type-specific fields. |
+ switch (received_type) { |
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: |
+ // COMPOSITION_START does not convey further information. |
+ break; |
+ |
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: |
+ case PP_INPUTEVENT_TYPE_IME_TEXT: |
+ // For COMPOSITION_END and TEXT, GetText() has meaning. |
+ return pp::Var(pp::PASS_REF, |
+ ime_input_event_interface_->GetText(received)) == |
+ pp::Var(pp::PASS_REF, |
+ ime_input_event_interface_->GetText(expected)); |
+ |
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: |
+ // For COMPOSITION_UPDATE, all fields must be checked. |
+ { |
+ uint32_t received_segment_number = |
+ ime_input_event_interface_->GetSegmentNumber(received); |
+ uint32_t expected_segment_number = |
+ ime_input_event_interface_->GetSegmentNumber(expected); |
+ if (received_segment_number != expected_segment_number) |
+ return false; |
+ |
+ // The "<=" is not a bug. i-th segment is represented as the pair of |
+ // i-th and (i+1)-th offsets in Pepper IME API. |
+ for (uint32_t i = 0; i <= received_segment_number; ++i) { |
+ if (ime_input_event_interface_->GetSegmentOffset(received, i) != |
+ ime_input_event_interface_->GetSegmentOffset(expected, i)) |
+ return false; |
+ } |
+ |
+ uint32_t received_selection_start = 0; |
+ uint32_t received_selection_end = 0; |
+ uint32_t expected_selection_start = 0; |
+ uint32_t expected_selection_end = 0; |
+ ime_input_event_interface_->GetSelection( |
+ received, &received_selection_start, &received_selection_end); |
+ ime_input_event_interface_->GetSelection( |
+ expected, &expected_selection_start, &expected_selection_end); |
+ if (received_selection_start != expected_selection_start || |
+ received_selection_end != expected_selection_end) { |
+ return true; |
+ } |
+ |
+ return pp::Var(pp::PASS_REF, |
+ ime_input_event_interface_->GetText(received)) == |
+ pp::Var(pp::PASS_REF, |
+ ime_input_event_interface_->GetText(expected)) && |
+ ime_input_event_interface_->GetTargetSegment(received) == |
+ ime_input_event_interface_->GetTargetSegment(expected); |
+ } |
+ |
+ case PP_INPUTEVENT_TYPE_CHAR: |
+ return |
+ keyboard_input_event_interface_->GetKeyCode(received) == |
+ keyboard_input_event_interface_->GetKeyCode(expected) && |
+ pp::Var(pp::PASS_REF, |
+ keyboard_input_event_interface_->GetCharacterText(received)) == |
+ pp::Var(pp::PASS_REF, |
+ keyboard_input_event_interface_->GetCharacterText(expected)); |
+ |
+ default: |
+ break; |
+ } |
+ return true; |
+} |
+ |
+std::string TestImeInputEvent::TestImeCommit() { |
+ GetFocusBySimulatingMouseClick(); |
+ |
+ input_event_interface_->RequestInputEvents(instance_->pp_instance(), |
+ PP_INPUTEVENT_CLASS_KEYBOARD | |
+ PP_INPUTEVENT_CLASS_IME); |
+ |
+ std::vector<uint32_t> segments; |
+ segments.push_back(0U); |
+ segments.push_back(3U); |
+ segments.push_back(6U); |
+ segments.push_back(9U); |
+ pp::InputEvent update_event = CreateImeCompositionUpdateEvent( |
+ kCompositionText, segments, 1, std::make_pair(3U, 6U)); |
+ |
+ expected_events_.clear(); |
+ expected_events_.push_back(CreateImeCompositionStartEvent()); |
+ expected_events_.push_back(update_event); |
+ expected_events_.push_back(CreateImeCompositionEndEvent(kCompositionText)); |
+ expected_events_.push_back(CreateImeTextEvent(kCompositionText)); |
+ |
+ // Simulate the case when IME successfully committed some text. |
+ ASSERT_TRUE(SimulateInputEvent(update_event)); |
+ ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText))); |
+ |
+ ASSERT_TRUE(expected_events_.empty()); |
+ PASS(); |
+} |
+ |
+std::string TestImeInputEvent::TestImeCancel() { |
+ GetFocusBySimulatingMouseClick(); |
+ |
+ input_event_interface_->RequestInputEvents(instance_->pp_instance(), |
+ PP_INPUTEVENT_CLASS_KEYBOARD | |
+ PP_INPUTEVENT_CLASS_IME); |
+ |
+ std::vector<uint32_t> segments; |
+ segments.push_back(0U); |
+ segments.push_back(3U); |
+ segments.push_back(6U); |
+ segments.push_back(9U); |
+ pp::InputEvent update_event = CreateImeCompositionUpdateEvent( |
+ kCompositionText, segments, 1, std::make_pair(3U, 6U)); |
+ |
+ expected_events_.clear(); |
+ expected_events_.push_back(CreateImeCompositionStartEvent()); |
+ expected_events_.push_back(update_event); |
+ expected_events_.push_back(CreateImeCompositionEndEvent("")); |
+ |
+ // Simulate the case when IME canceled composition. |
+ ASSERT_TRUE(SimulateInputEvent(update_event)); |
+ ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(""))); |
+ |
+ ASSERT_TRUE(expected_events_.empty()); |
+ PASS(); |
+} |
+ |
+std::string TestImeInputEvent::TestImeUnawareCommit() { |
+ GetFocusBySimulatingMouseClick(); |
+ |
+ input_event_interface_->ClearInputEventRequest(instance_->pp_instance(), |
+ PP_INPUTEVENT_CLASS_IME); |
+ input_event_interface_->RequestInputEvents(instance_->pp_instance(), |
+ PP_INPUTEVENT_CLASS_KEYBOARD); |
+ |
+ std::vector<uint32_t> segments; |
+ segments.push_back(0U); |
+ segments.push_back(3U); |
+ segments.push_back(6U); |
+ segments.push_back(9U); |
+ pp::InputEvent update_event = CreateImeCompositionUpdateEvent( |
+ kCompositionText, segments, 1, std::make_pair(3U, 6U)); |
+ |
+ expected_events_.clear(); |
+ expected_events_.push_back(CreateCharEvent(kCompositionChar[0])); |
+ expected_events_.push_back(CreateCharEvent(kCompositionChar[1])); |
+ expected_events_.push_back(CreateCharEvent(kCompositionChar[2])); |
+ |
+ // Test for IME-unaware plugins. Commit event is translated to char events. |
+ ASSERT_TRUE(SimulateInputEvent(update_event)); |
+ ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText))); |
+ |
+ ASSERT_TRUE(expected_events_.empty()); |
+ PASS(); |
+} |
+ |
+ |
+std::string TestImeInputEvent::TestImeUnawareCancel() { |
+ GetFocusBySimulatingMouseClick(); |
+ |
+ input_event_interface_->ClearInputEventRequest(instance_->pp_instance(), |
+ PP_INPUTEVENT_CLASS_IME); |
+ input_event_interface_->RequestInputEvents(instance_->pp_instance(), |
+ PP_INPUTEVENT_CLASS_KEYBOARD); |
+ |
+ std::vector<uint32_t> segments; |
+ segments.push_back(0U); |
+ segments.push_back(3U); |
+ segments.push_back(6U); |
+ segments.push_back(9U); |
+ pp::InputEvent update_event = CreateImeCompositionUpdateEvent( |
+ kCompositionText, segments, 1, std::make_pair(3U, 6U)); |
+ |
+ expected_events_.clear(); |
+ |
+ // Test for IME-unaware plugins. Cancel won't issue any events. |
+ ASSERT_TRUE(SimulateInputEvent(update_event)); |
+ ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(""))); |
+ |
+ ASSERT_TRUE(expected_events_.empty()); |
+ PASS(); |
+} |
+ |