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.
5 #include "ppapi/tests/test_input_event.h"
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"
14 REGISTER_TEST_CASE(InputEvent
);
18 const uint32_t kSpaceChar
= 0x20;
19 const char* kSpaceString
= " ";
20 const char* kSpaceCode
= "Space";
22 #define FINISHED_WAITING_MESSAGE "TEST_INPUT_EVENT_FINISHED_WAITING"
24 pp::Point
GetCenter(const pp::Rect
& rect
) {
26 rect
.x() + rect
.width() / 2,
27 rect
.y() + rect
.height() / 2);
32 void TestInputEvent::RunTests(const std::string
& filter
) {
33 RUN_TEST(Events
, filter
);
34 RUN_TEST(EventsLatencyTracking
, filter
);
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
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
);
48 TestInputEvent::TestInputEvent(TestingInstance
* 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()),
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) {
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.
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
);
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
));
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();
102 // Set up a listener for our message that signals that all input events have
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
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
"');"
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
);
125 pp::InputEvent
TestInputEvent::CreateMouseEvent(
126 PP_InputEvent_Type type
,
127 PP_InputEvent_MouseButton buttons
) {
128 return pp::MouseInputEvent(
134 GetCenter(view_rect_
),
136 pp::Point()); // movement
139 pp::InputEvent
TestInputEvent::CreateWheelEvent() {
140 return pp::WheelInputEvent(
144 pp::FloatPoint(1, 2),
145 pp::FloatPoint(3, 4),
146 PP_TRUE
); // scroll_by_page
149 pp::InputEvent
TestInputEvent::CreateKeyEvent(PP_InputEvent_Type type
,
151 const std::string
& code
) {
152 return pp::KeyboardInputEvent(
162 pp::InputEvent
TestInputEvent::CreateCharEvent(const std::string
& text
) {
163 return pp::KeyboardInputEvent(
165 PP_INPUTEVENT_TYPE_CHAR
,
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
;
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
);
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();
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_
;
205 bool TestInputEvent::AreEquivalentEvents(PP_Resource received
,
206 PP_Resource expected
) {
207 if (!input_event_interface_
->IsInputEvent(received
) ||
208 !input_event_interface_
->IsInputEvent(expected
)) {
212 // Test common fields, except modifiers and time stamp, which may be changed
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
) {
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.
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
);
239 case PP_INPUTEVENT_TYPE_WHEEL
:
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
);
248 case PP_INPUTEVENT_TYPE_RAWKEYDOWN
:
249 case PP_INPUTEVENT_TYPE_KEYDOWN
:
250 case PP_INPUTEVENT_TYPE_KEYUP
:
252 keyboard_input_event_interface_
->GetKeyCode(received
) ==
253 keyboard_input_event_interface_
->GetKeyCode(expected
);
255 case PP_INPUTEVENT_TYPE_CHAR
:
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
));
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
))
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
))
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
);
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
)
291 if (expected_point
.position
.x
!= received_point
.position
.x
||
292 expected_point
.position
.y
!= received_point
.position
.y
)
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());
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);
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();
330 void TestInputEvent::DidChangeView(const pp::View
& view
) {
331 view_rect_
= view
.GetRect();
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();
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_
);
346 input_event_private_interface_
->StartTrackingLatency(
347 instance_
->pp_instance());
349 ASSERT_TRUE(SimulateInputEvent(CreateTouchEvent(PP_INPUTEVENT_TYPE_TOUCHSTART
,
350 pp::FloatPoint(12, 23))));
351 ASSERT_TRUE(last_latency_tracking_successful_
);
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();
365 // Send the events and check that we received them.
367 SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN
,
368 PP_INPUTEVENT_MOUSEBUTTON_LEFT
)));
370 SimulateInputEvent(CreateWheelEvent()));
372 SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN
,
373 kSpaceChar
, kSpaceCode
)));
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();
384 // Check that we only receive mouse events.
386 SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN
,
387 PP_INPUTEVENT_MOUSEBUTTON_LEFT
)));
389 SimulateInputEvent(CreateWheelEvent()));
391 SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN
,
392 kSpaceChar
, kSpaceCode
)));
394 SimulateInputEvent(CreateCharEvent(kSpaceString
)));
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
);
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
);
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
);
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
);