• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetCenter(const pp::Rect & rect)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 
RunTests(const std::string & filter)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 
TestInputEvent(TestingInstance * instance)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 
~TestInputEvent()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 
Init()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 
CreateMouseEvent(PP_InputEvent_Type type,PP_InputEvent_MouseButton buttons)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 
CreateWheelEvent()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 
CreateKeyEvent(PP_InputEvent_Type type,uint32_t key_code,const std::string & code)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 
CreateCharEvent(const std::string & text)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 
CreateTouchEvent(PP_InputEvent_Type type,const pp::FloatPoint & point)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 
PostMessageBarrier()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.
SimulateInputEvent(const pp::InputEvent & input_event)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 
AreEquivalentEvents(PP_Resource received,PP_Resource expected)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 
HandleInputEvent(const pp::InputEvent & input_event)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 
HandleMessage(const pp::Var & message_data)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 
DidChangeView(const pp::View & view)330 void TestInputEvent::DidChangeView(const pp::View& view) {
331   view_rect_ = view.GetRect();
332 }
333 
TestEventsLatencyTracking()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 
TestEvents()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 
TestAcceptTouchEvent_1()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 
TestAcceptTouchEvent_2()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 
TestAcceptTouchEvent_3()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 
TestAcceptTouchEvent_4()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