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