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