| 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();
|
| +}
|
| +
|
|
|