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