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_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(InputEvent);
     15 
     16 namespace {
     17 
     18 const uint32_t kSpaceChar = 0x20;
     19 const char* kSpaceString = " ";
     20 
     21 #define FINISHED_WAITING_MESSAGE "TEST_INPUT_EVENT_FINISHED_WAITING"
     22 
     23 pp::Point GetCenter(const pp::Rect& rect) {
     24   return pp::Point(
     25       rect.x() + rect.width() / 2,
     26       rect.y() + rect.height() / 2);
     27 }
     28 
     29 }  // namespace
     30 
     31 void TestInputEvent::RunTests(const std::string& filter) {
     32   RUN_TEST(Events, filter);
     33 
     34   // The AcceptTouchEvent_N tests should not be run when the filter is empty;
     35   // they can only be run one at a time.
     36   // TODO(dmichael): Figure out a way to make these run in the same test fixture
     37   //                 instance.
     38   if (!ShouldRunAllTests(filter)) {
     39     RUN_TEST(AcceptTouchEvent_1, filter);
     40     RUN_TEST(AcceptTouchEvent_2, filter);
     41     RUN_TEST(AcceptTouchEvent_3, filter);
     42     RUN_TEST(AcceptTouchEvent_4, filter);
     43   }
     44 }
     45 
     46 TestInputEvent::TestInputEvent(TestingInstance* instance)
     47     : TestCase(instance),
     48       input_event_interface_(NULL),
     49       mouse_input_event_interface_(NULL),
     50       wheel_input_event_interface_(NULL),
     51       keyboard_input_event_interface_(NULL),
     52       touch_input_event_interface_(NULL),
     53       nested_event_(instance->pp_instance()),
     54       view_rect_(),
     55       expected_input_event_(0),
     56       received_expected_event_(false),
     57       received_finish_message_(false) {
     58 }
     59 
     60 TestInputEvent::~TestInputEvent() {
     61   // Remove the special listener that only responds to a
     62   // FINISHED_WAITING_MESSAGE string. See Init for where it gets added.
     63   std::string js_code;
     64   js_code += "var plugin = document.getElementById('plugin');"
     65              "plugin.removeEventListener('message',"
     66              "                           plugin.wait_for_messages_handler);"
     67              "delete plugin.wait_for_messages_handler;";
     68   instance_->EvalScript(js_code);
     69 }
     70 
     71 bool TestInputEvent::Init() {
     72   input_event_interface_ = static_cast<const PPB_InputEvent*>(
     73       pp::Module::Get()->GetBrowserInterface(PPB_INPUT_EVENT_INTERFACE));
     74   mouse_input_event_interface_ = static_cast<const PPB_MouseInputEvent*>(
     75       pp::Module::Get()->GetBrowserInterface(
     76           PPB_MOUSE_INPUT_EVENT_INTERFACE));
     77   wheel_input_event_interface_ = static_cast<const PPB_WheelInputEvent*>(
     78       pp::Module::Get()->GetBrowserInterface(
     79           PPB_WHEEL_INPUT_EVENT_INTERFACE));
     80   keyboard_input_event_interface_ = static_cast<const PPB_KeyboardInputEvent*>(
     81       pp::Module::Get()->GetBrowserInterface(
     82           PPB_KEYBOARD_INPUT_EVENT_INTERFACE));
     83   touch_input_event_interface_ = static_cast<const PPB_TouchInputEvent*>(
     84       pp::Module::Get()->GetBrowserInterface(
     85           PPB_TOUCH_INPUT_EVENT_INTERFACE));
     86 
     87   bool success =
     88       input_event_interface_ &&
     89       mouse_input_event_interface_ &&
     90       wheel_input_event_interface_ &&
     91       keyboard_input_event_interface_ &&
     92       touch_input_event_interface_ &&
     93       CheckTestingInterface();
     94 
     95   // Set up a listener for our message that signals that all input events have
     96   // been received.
     97   std::string js_code;
     98   // Note the following code is dependent on some features of test_case.html.
     99   // E.g., it is assumed that the DOM element where the plugin is embedded has
    100   // an id of 'plugin', and there is a function 'IsTestingMessage' that allows
    101   // us to ignore the messages that are intended for use by the testing
    102   // framework itself.
    103   js_code += "var plugin = document.getElementById('plugin');"
    104              "var wait_for_messages_handler = function(message_event) {"
    105              "  if (!IsTestingMessage(message_event.data) &&"
    106              "      message_event.data === '" FINISHED_WAITING_MESSAGE "') {"
    107              "    plugin.postMessage('" FINISHED_WAITING_MESSAGE "');"
    108              "  }"
    109              "};"
    110              "plugin.addEventListener('message', wait_for_messages_handler);"
    111              // Stash it on the plugin so we can remove it in the destructor.
    112              "plugin.wait_for_messages_handler = wait_for_messages_handler;";
    113   instance_->EvalScript(js_code);
    114 
    115   return success;
    116 }
    117 
    118 pp::InputEvent TestInputEvent::CreateMouseEvent(
    119     PP_InputEvent_Type type,
    120     PP_InputEvent_MouseButton buttons) {
    121   return pp::MouseInputEvent(
    122       instance_,
    123       type,
    124       100,  // time_stamp
    125       0,  // modifiers
    126       buttons,
    127       GetCenter(view_rect_),
    128       1,  // click count
    129       pp::Point());  // movement
    130 }
    131 
    132 pp::InputEvent TestInputEvent::CreateWheelEvent() {
    133   return pp::WheelInputEvent(
    134       instance_,
    135       100,  // time_stamp
    136       0,  // modifiers
    137       pp::FloatPoint(1, 2),
    138       pp::FloatPoint(3, 4),
    139       PP_TRUE);  // scroll_by_page
    140 }
    141 
    142 pp::InputEvent TestInputEvent::CreateKeyEvent(PP_InputEvent_Type type,
    143                                               uint32_t key_code) {
    144   return pp::KeyboardInputEvent(
    145       instance_,
    146       type,
    147       100,  // time_stamp
    148       0,  // modifiers
    149       key_code,
    150       pp::Var());
    151 }
    152 
    153 pp::InputEvent TestInputEvent::CreateCharEvent(const std::string& text) {
    154   return pp::KeyboardInputEvent(
    155       instance_,
    156       PP_INPUTEVENT_TYPE_CHAR,
    157       100,  // time_stamp
    158       0,  // modifiers
    159       0,  // keycode
    160       pp::Var(text));
    161 }
    162 
    163 pp::InputEvent TestInputEvent::CreateTouchEvent(PP_InputEvent_Type type,
    164                                                 const pp::FloatPoint& point) {
    165   PP_TouchPoint touch_point = PP_MakeTouchPoint();
    166   touch_point.position = point;
    167 
    168   pp::TouchInputEvent touch_event(instance_, type, 100, 0);
    169   touch_event.AddTouchPoint(PP_TOUCHLIST_TYPE_TOUCHES, touch_point);
    170   touch_event.AddTouchPoint(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, touch_point);
    171   touch_event.AddTouchPoint(PP_TOUCHLIST_TYPE_TARGETTOUCHES, touch_point);
    172 
    173   return touch_event;
    174 }
    175 
    176 void TestInputEvent::PostMessageBarrier() {
    177   received_finish_message_ = false;
    178   instance_->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE));
    179   testing_interface_->RunMessageLoop(instance_->pp_instance());
    180   nested_event_.Wait();
    181 }
    182 
    183 // Simulates the input event and calls PostMessage to let us know when
    184 // we have received all resulting events from the browser.
    185 bool TestInputEvent::SimulateInputEvent(
    186     const pp::InputEvent& input_event) {
    187   expected_input_event_ = pp::InputEvent(input_event.pp_resource());
    188   received_expected_event_ = false;
    189   testing_interface_->SimulateInputEvent(instance_->pp_instance(),
    190                                          input_event.pp_resource());
    191   PostMessageBarrier();
    192   return received_expected_event_;
    193 }
    194 
    195 bool TestInputEvent::AreEquivalentEvents(PP_Resource received,
    196                                          PP_Resource expected) {
    197   if (!input_event_interface_->IsInputEvent(received) ||
    198       !input_event_interface_->IsInputEvent(expected)) {
    199     return false;
    200   }
    201 
    202   // Test common fields, except modifiers and time stamp, which may be changed
    203   // by the browser.
    204   int32_t received_type = input_event_interface_->GetType(received);
    205   int32_t expected_type = input_event_interface_->GetType(expected);
    206   if (received_type != expected_type) {
    207     // Allow key down events to match "raw" key down events.
    208     if (expected_type != PP_INPUTEVENT_TYPE_KEYDOWN &&
    209         received_type != PP_INPUTEVENT_TYPE_RAWKEYDOWN) {
    210       return false;
    211     }
    212   }
    213 
    214   // Test event type-specific fields.
    215   switch (input_event_interface_->GetType(received)) {
    216     case PP_INPUTEVENT_TYPE_MOUSEDOWN:
    217     case PP_INPUTEVENT_TYPE_MOUSEUP:
    218     case PP_INPUTEVENT_TYPE_MOUSEMOVE:
    219     case PP_INPUTEVENT_TYPE_MOUSEENTER:
    220     case PP_INPUTEVENT_TYPE_MOUSELEAVE:
    221       // Check mouse fields, except position and movement, which may be
    222       // modified by the renderer.
    223       return
    224           mouse_input_event_interface_->GetButton(received) ==
    225           mouse_input_event_interface_->GetButton(expected) &&
    226           mouse_input_event_interface_->GetClickCount(received) ==
    227           mouse_input_event_interface_->GetClickCount(expected);
    228 
    229     case PP_INPUTEVENT_TYPE_WHEEL:
    230       return
    231           pp::FloatPoint(wheel_input_event_interface_->GetDelta(received)) ==
    232           pp::FloatPoint(wheel_input_event_interface_->GetDelta(expected)) &&
    233           pp::FloatPoint(wheel_input_event_interface_->GetTicks(received)) ==
    234           pp::FloatPoint(wheel_input_event_interface_->GetTicks(expected)) &&
    235           wheel_input_event_interface_->GetScrollByPage(received) ==
    236           wheel_input_event_interface_->GetScrollByPage(expected);
    237 
    238     case PP_INPUTEVENT_TYPE_RAWKEYDOWN:
    239     case PP_INPUTEVENT_TYPE_KEYDOWN:
    240     case PP_INPUTEVENT_TYPE_KEYUP:
    241       return
    242           keyboard_input_event_interface_->GetKeyCode(received) ==
    243           keyboard_input_event_interface_->GetKeyCode(expected);
    244 
    245     case PP_INPUTEVENT_TYPE_CHAR:
    246       return
    247           keyboard_input_event_interface_->GetKeyCode(received) ==
    248           keyboard_input_event_interface_->GetKeyCode(expected) &&
    249           pp::Var(pp::PASS_REF,
    250               keyboard_input_event_interface_->GetCharacterText(received)) ==
    251           pp::Var(pp::PASS_REF,
    252               keyboard_input_event_interface_->GetCharacterText(expected));
    253 
    254     case PP_INPUTEVENT_TYPE_TOUCHSTART:
    255     case PP_INPUTEVENT_TYPE_TOUCHMOVE:
    256     case PP_INPUTEVENT_TYPE_TOUCHEND:
    257     case PP_INPUTEVENT_TYPE_TOUCHCANCEL: {
    258       if (!touch_input_event_interface_->IsTouchInputEvent(received) ||
    259           !touch_input_event_interface_->IsTouchInputEvent(expected))
    260         return false;
    261 
    262       uint32_t touch_count = touch_input_event_interface_->GetTouchCount(
    263           received, PP_TOUCHLIST_TYPE_TOUCHES);
    264       if (touch_count <= 0 ||
    265           touch_count != touch_input_event_interface_->GetTouchCount(expected,
    266               PP_TOUCHLIST_TYPE_TOUCHES))
    267         return false;
    268 
    269       for (uint32_t i = 0; i < touch_count; ++i) {
    270         PP_TouchPoint expected_point = touch_input_event_interface_->
    271             GetTouchByIndex(expected, PP_TOUCHLIST_TYPE_TOUCHES, i);
    272         PP_TouchPoint received_point = touch_input_event_interface_->
    273             GetTouchByIndex(received, PP_TOUCHLIST_TYPE_TOUCHES, i);
    274 
    275         if (expected_point.id != received_point.id ||
    276             expected_point.radius != received_point.radius ||
    277             expected_point.rotation_angle != received_point.rotation_angle ||
    278             expected_point.pressure != received_point.pressure)
    279           return false;
    280 
    281         if (expected_point.position.x != received_point.position.x ||
    282             expected_point.position.y != received_point.position.y)
    283           return false;
    284       }
    285       return true;
    286     }
    287 
    288     default:
    289       break;
    290   }
    291 
    292   return false;
    293 }
    294 
    295 bool TestInputEvent::HandleInputEvent(const pp::InputEvent& input_event) {
    296   // Some events may cause extra events to be generated, so look for the
    297   // first one that matches.
    298   if (!received_expected_event_) {
    299     received_expected_event_ = AreEquivalentEvents(
    300         input_event.pp_resource(),
    301         expected_input_event_.pp_resource());
    302   }
    303   // Handle all input events.
    304   return true;
    305 }
    306 
    307 void TestInputEvent::HandleMessage(const pp::Var& message_data) {
    308   if (message_data.is_string() &&
    309       (message_data.AsString() == FINISHED_WAITING_MESSAGE)) {
    310     testing_interface_->QuitMessageLoop(instance_->pp_instance());
    311     received_finish_message_ = true;
    312     nested_event_.Signal();
    313   }
    314 }
    315 
    316 void TestInputEvent::DidChangeView(const pp::View& view) {
    317   view_rect_ = view.GetRect();
    318 }
    319 
    320 std::string TestInputEvent::TestEvents() {
    321   // Request all input event classes.
    322   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
    323                                              PP_INPUTEVENT_CLASS_MOUSE |
    324                                              PP_INPUTEVENT_CLASS_WHEEL |
    325                                              PP_INPUTEVENT_CLASS_KEYBOARD |
    326                                              PP_INPUTEVENT_CLASS_TOUCH);
    327   PostMessageBarrier();
    328 
    329   // Send the events and check that we received them.
    330   ASSERT_TRUE(
    331       SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN,
    332                                           PP_INPUTEVENT_MOUSEBUTTON_LEFT)));
    333   ASSERT_TRUE(
    334       SimulateInputEvent(CreateWheelEvent()));
    335   ASSERT_TRUE(
    336       SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN,
    337                                         kSpaceChar)));
    338   ASSERT_TRUE(
    339       SimulateInputEvent(CreateCharEvent(kSpaceString)));
    340   ASSERT_TRUE(SimulateInputEvent(CreateTouchEvent(PP_INPUTEVENT_TYPE_TOUCHSTART,
    341                                                   pp::FloatPoint(12, 23))));
    342   // Request only mouse events.
    343   input_event_interface_->ClearInputEventRequest(instance_->pp_instance(),
    344                                                  PP_INPUTEVENT_CLASS_WHEEL |
    345                                                  PP_INPUTEVENT_CLASS_KEYBOARD);
    346   PostMessageBarrier();
    347 
    348   // Check that we only receive mouse events.
    349   ASSERT_TRUE(
    350       SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN,
    351                                           PP_INPUTEVENT_MOUSEBUTTON_LEFT)));
    352   ASSERT_FALSE(
    353       SimulateInputEvent(CreateWheelEvent()));
    354   ASSERT_FALSE(
    355       SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN,
    356                                         kSpaceChar)));
    357   ASSERT_FALSE(
    358       SimulateInputEvent(CreateCharEvent(kSpaceString)));
    359 
    360   PASS();
    361 }
    362 
    363 std::string TestInputEvent::TestAcceptTouchEvent_1() {
    364   // The browser normally sends touch-events to the renderer only if the page
    365   // has touch-event handlers. Since test-case.html does not have any
    366   // touch-event handler, it would normally not receive any touch events from
    367   // the browser. However, if a plugin in the page does accept touch events,
    368   // then the browser should start sending touch-events to the page. In this
    369   // test, the plugin simply registers for touch-events. The real test is to
    370   // verify that the browser knows to send touch-events to the renderer.
    371   // If the plugin is removed from the page, then there are no more touch-event
    372   // handlers in the page, and browser stops sending touch-events. So to make
    373   // it possible to test this properly, the plugin is not removed from the page
    374   // at the end of the test.
    375   instance_->set_remove_plugin(false);
    376   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
    377                                              PP_INPUTEVENT_CLASS_MOUSE |
    378                                              PP_INPUTEVENT_CLASS_WHEEL |
    379                                              PP_INPUTEVENT_CLASS_KEYBOARD |
    380                                              PP_INPUTEVENT_CLASS_TOUCH);
    381   PASS();
    382 }
    383 
    384 std::string TestInputEvent::TestAcceptTouchEvent_2() {
    385   // See comment in TestAcceptTouchEvent_1.
    386   instance_->set_remove_plugin(false);
    387   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
    388                                              PP_INPUTEVENT_CLASS_MOUSE |
    389                                              PP_INPUTEVENT_CLASS_WHEEL |
    390                                              PP_INPUTEVENT_CLASS_KEYBOARD |
    391                                              PP_INPUTEVENT_CLASS_TOUCH);
    392   input_event_interface_->ClearInputEventRequest(instance_->pp_instance(),
    393                                                  PP_INPUTEVENT_CLASS_TOUCH);
    394   PASS();
    395 }
    396 
    397 std::string TestInputEvent::TestAcceptTouchEvent_3() {
    398   // See comment in TestAcceptTouchEvent_1.
    399   instance_->set_remove_plugin(false);
    400   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
    401                                              PP_INPUTEVENT_CLASS_MOUSE |
    402                                              PP_INPUTEVENT_CLASS_WHEEL |
    403                                              PP_INPUTEVENT_CLASS_KEYBOARD);
    404   input_event_interface_->RequestFilteringInputEvents(instance_->pp_instance(),
    405       PP_INPUTEVENT_CLASS_TOUCH);
    406   PASS();
    407 }
    408 
    409 std::string TestInputEvent::TestAcceptTouchEvent_4() {
    410   // See comment in TestAcceptTouchEvent_1.
    411   instance_->set_remove_plugin(false);
    412   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
    413                                              PP_INPUTEVENT_CLASS_MOUSE |
    414                                              PP_INPUTEVENT_CLASS_WHEEL |
    415                                              PP_INPUTEVENT_CLASS_KEYBOARD);
    416   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
    417                                              PP_INPUTEVENT_CLASS_TOUCH);
    418   PASS();
    419 }
    420 
    421