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/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"
15 REGISTER_TEST_CASE(InputEvent
);
19 const uint32_t kSpaceChar
= 0x20;
20 const char* kSpaceString
= " ";
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
);
35 // Like RUN_TEST, but does an exact match with the filter (which means it does
36 // not run the test if filter is empty).
37 #define RUN_TEST_EXACT_MATCH(name, test_filter) \
38 if (test_filter == #name) { \
39 set_callback_type(PP_OPTIONAL); \
40 instance_->LogTest(#name, CheckResourcesAndVars(Test##name())); \
43 RUN_TEST_EXACT_MATCH(AcceptTouchEvent_1
, filter
);
44 RUN_TEST_EXACT_MATCH(AcceptTouchEvent_2
, filter
);
45 RUN_TEST_EXACT_MATCH(AcceptTouchEvent_3
, filter
);
46 RUN_TEST_EXACT_MATCH(AcceptTouchEvent_4
, filter
);
48 #undef RUN_TEST_EXACT_MATCH
51 TestInputEvent::TestInputEvent(TestingInstance
* instance
)
53 input_event_interface_(NULL
),
54 mouse_input_event_interface_(NULL
),
55 wheel_input_event_interface_(NULL
),
56 keyboard_input_event_interface_(NULL
),
57 touch_input_event_interface_(NULL
),
59 expected_input_event_(0),
60 received_expected_event_(false),
61 received_finish_message_(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 mouse_input_event_interface_
= static_cast<const PPB_MouseInputEvent
*>(
79 pp::Module::Get()->GetBrowserInterface(
80 PPB_MOUSE_INPUT_EVENT_INTERFACE
));
81 wheel_input_event_interface_
= static_cast<const PPB_WheelInputEvent
*>(
82 pp::Module::Get()->GetBrowserInterface(
83 PPB_WHEEL_INPUT_EVENT_INTERFACE
));
84 keyboard_input_event_interface_
= static_cast<const PPB_KeyboardInputEvent
*>(
85 pp::Module::Get()->GetBrowserInterface(
86 PPB_KEYBOARD_INPUT_EVENT_INTERFACE
));
87 touch_input_event_interface_
= static_cast<const PPB_TouchInputEvent
*>(
88 pp::Module::Get()->GetBrowserInterface(
89 PPB_TOUCH_INPUT_EVENT_INTERFACE
));
92 input_event_interface_
&&
93 mouse_input_event_interface_
&&
94 wheel_input_event_interface_
&&
95 keyboard_input_event_interface_
&&
96 touch_input_event_interface_
&&
97 CheckTestingInterface();
99 // Set up a listener for our message that signals that all input events have
102 // Note the following code is dependent on some features of test_case.html.
103 // E.g., it is assumed that the DOM element where the plugin is embedded has
104 // an id of 'plugin', and there is a function 'IsTestingMessage' that allows
105 // us to ignore the messages that are intended for use by the testing
107 js_code
+= "var plugin = document.getElementById('plugin');"
108 "var wait_for_messages_handler = function(message_event) {"
109 " if (!IsTestingMessage(message_event.data) &&"
110 " message_event.data === '" FINISHED_WAITING_MESSAGE
"') {"
111 " plugin.postMessage('" FINISHED_WAITING_MESSAGE
"');"
114 "plugin.addEventListener('message', wait_for_messages_handler);"
115 // Stash it on the plugin so we can remove it in the destructor.
116 "plugin.wait_for_messages_handler = wait_for_messages_handler;";
117 instance_
->EvalScript(js_code
);
122 pp::InputEvent
TestInputEvent::CreateMouseEvent(
123 PP_InputEvent_Type type
,
124 PP_InputEvent_MouseButton buttons
) {
125 return pp::MouseInputEvent(
131 GetCenter(view_rect_
),
133 pp::Point()); // movement
136 pp::InputEvent
TestInputEvent::CreateWheelEvent() {
137 return pp::WheelInputEvent(
141 pp::FloatPoint(1, 2),
142 pp::FloatPoint(3, 4),
143 PP_TRUE
); // scroll_by_page
146 pp::InputEvent
TestInputEvent::CreateKeyEvent(PP_InputEvent_Type type
,
148 return pp::KeyboardInputEvent(
157 pp::InputEvent
TestInputEvent::CreateCharEvent(const std::string
& text
) {
158 return pp::KeyboardInputEvent(
160 PP_INPUTEVENT_TYPE_CHAR
,
167 pp::InputEvent
TestInputEvent::CreateTouchEvent(PP_InputEvent_Type type
,
168 const pp::FloatPoint
& point
) {
169 PP_TouchPoint touch_point
= PP_MakeTouchPoint();
170 touch_point
.position
= point
;
172 pp::TouchInputEvent
touch_event(instance_
, type
, 100, 0);
173 touch_event
.AddTouchPoint(PP_TOUCHLIST_TYPE_TOUCHES
, touch_point
);
174 touch_event
.AddTouchPoint(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES
, touch_point
);
175 touch_event
.AddTouchPoint(PP_TOUCHLIST_TYPE_TARGETTOUCHES
, touch_point
);
180 // Simulates the input event and calls PostMessage to let us know when
181 // we have received all resulting events from the browser.
182 bool TestInputEvent::SimulateInputEvent(
183 const pp::InputEvent
& input_event
) {
184 expected_input_event_
= pp::InputEvent(input_event
.pp_resource());
185 received_expected_event_
= false;
186 received_finish_message_
= false;
187 testing_interface_
->SimulateInputEvent(instance_
->pp_instance(),
188 input_event
.pp_resource());
189 instance_
->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE
));
190 testing_interface_
->RunMessageLoop(instance_
->pp_instance());
191 return received_finish_message_
&& received_expected_event_
;
194 bool TestInputEvent::AreEquivalentEvents(PP_Resource received
,
195 PP_Resource expected
) {
196 if (!input_event_interface_
->IsInputEvent(received
) ||
197 !input_event_interface_
->IsInputEvent(expected
)) {
201 // Test common fields, except modifiers and time stamp, which may be changed
203 int32_t received_type
= input_event_interface_
->GetType(received
);
204 int32_t expected_type
= input_event_interface_
->GetType(expected
);
205 if (received_type
!= expected_type
) {
206 // Allow key down events to match "raw" key down events.
207 if (expected_type
!= PP_INPUTEVENT_TYPE_KEYDOWN
&&
208 received_type
!= PP_INPUTEVENT_TYPE_RAWKEYDOWN
) {
213 // Test event type-specific fields.
214 switch (input_event_interface_
->GetType(received
)) {
215 case PP_INPUTEVENT_TYPE_MOUSEDOWN
:
216 case PP_INPUTEVENT_TYPE_MOUSEUP
:
217 case PP_INPUTEVENT_TYPE_MOUSEMOVE
:
218 case PP_INPUTEVENT_TYPE_MOUSEENTER
:
219 case PP_INPUTEVENT_TYPE_MOUSELEAVE
:
220 // Check mouse fields, except position and movement, which may be
221 // modified by the renderer.
223 mouse_input_event_interface_
->GetButton(received
) ==
224 mouse_input_event_interface_
->GetButton(expected
) &&
225 mouse_input_event_interface_
->GetClickCount(received
) ==
226 mouse_input_event_interface_
->GetClickCount(expected
);
228 case PP_INPUTEVENT_TYPE_WHEEL
:
230 pp::FloatPoint(wheel_input_event_interface_
->GetDelta(received
)) ==
231 pp::FloatPoint(wheel_input_event_interface_
->GetDelta(expected
)) &&
232 pp::FloatPoint(wheel_input_event_interface_
->GetTicks(received
)) ==
233 pp::FloatPoint(wheel_input_event_interface_
->GetTicks(expected
)) &&
234 wheel_input_event_interface_
->GetScrollByPage(received
) ==
235 wheel_input_event_interface_
->GetScrollByPage(expected
);
237 case PP_INPUTEVENT_TYPE_RAWKEYDOWN
:
238 case PP_INPUTEVENT_TYPE_KEYDOWN
:
239 case PP_INPUTEVENT_TYPE_KEYUP
:
241 keyboard_input_event_interface_
->GetKeyCode(received
) ==
242 keyboard_input_event_interface_
->GetKeyCode(expected
);
244 case PP_INPUTEVENT_TYPE_CHAR
:
246 keyboard_input_event_interface_
->GetKeyCode(received
) ==
247 keyboard_input_event_interface_
->GetKeyCode(expected
) &&
248 pp::Var(pp::PASS_REF
,
249 keyboard_input_event_interface_
->GetCharacterText(received
)) ==
250 pp::Var(pp::PASS_REF
,
251 keyboard_input_event_interface_
->GetCharacterText(expected
));
253 case PP_INPUTEVENT_TYPE_TOUCHSTART
:
254 case PP_INPUTEVENT_TYPE_TOUCHMOVE
:
255 case PP_INPUTEVENT_TYPE_TOUCHEND
:
256 case PP_INPUTEVENT_TYPE_TOUCHCANCEL
: {
257 if (!touch_input_event_interface_
->IsTouchInputEvent(received
) ||
258 !touch_input_event_interface_
->IsTouchInputEvent(expected
))
261 uint32_t touch_count
= touch_input_event_interface_
->GetTouchCount(
262 received
, PP_TOUCHLIST_TYPE_TOUCHES
);
263 if (touch_count
<= 0 ||
264 touch_count
!= touch_input_event_interface_
->GetTouchCount(expected
,
265 PP_TOUCHLIST_TYPE_TOUCHES
))
268 for (uint32_t i
= 0; i
< touch_count
; ++i
) {
269 PP_TouchPoint expected_point
= touch_input_event_interface_
->
270 GetTouchByIndex(expected
, PP_TOUCHLIST_TYPE_TOUCHES
, i
);
271 PP_TouchPoint received_point
= touch_input_event_interface_
->
272 GetTouchByIndex(received
, PP_TOUCHLIST_TYPE_TOUCHES
, i
);
274 if (expected_point
.id
!= received_point
.id
||
275 expected_point
.radius
!= received_point
.radius
||
276 expected_point
.rotation_angle
!= received_point
.rotation_angle
||
277 expected_point
.pressure
!= received_point
.pressure
)
280 if (expected_point
.position
.x
!= received_point
.position
.x
||
281 expected_point
.position
.y
!= received_point
.position
.y
)
294 bool TestInputEvent::HandleInputEvent(const pp::InputEvent
& input_event
) {
295 // Some events may cause extra events to be generated, so look for the
296 // first one that matches.
297 if (!received_expected_event_
) {
298 received_expected_event_
= AreEquivalentEvents(
299 input_event
.pp_resource(),
300 expected_input_event_
.pp_resource());
302 // Handle all input events.
306 void TestInputEvent::HandleMessage(const pp::Var
& message_data
) {
307 if (message_data
.is_string() &&
308 (message_data
.AsString() == FINISHED_WAITING_MESSAGE
)) {
309 testing_interface_
->QuitMessageLoop(instance_
->pp_instance());
310 received_finish_message_
= true;
314 void TestInputEvent::DidChangeView(const pp::View
& view
) {
315 view_rect_
= view
.GetRect();
318 std::string
TestInputEvent::TestEvents() {
319 // Request all input event classes.
320 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
321 PP_INPUTEVENT_CLASS_MOUSE
|
322 PP_INPUTEVENT_CLASS_WHEEL
|
323 PP_INPUTEVENT_CLASS_KEYBOARD
|
324 PP_INPUTEVENT_CLASS_TOUCH
);
325 // Send the events and check that we received them.
327 SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN
,
328 PP_INPUTEVENT_MOUSEBUTTON_LEFT
)));
330 SimulateInputEvent(CreateWheelEvent()));
332 SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN
,
335 SimulateInputEvent(CreateCharEvent(kSpaceString
)));
336 ASSERT_TRUE(SimulateInputEvent(CreateTouchEvent(PP_INPUTEVENT_TYPE_TOUCHSTART
,
337 pp::FloatPoint(12, 23))));
338 // Request only mouse events.
339 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
340 PP_INPUTEVENT_CLASS_WHEEL
|
341 PP_INPUTEVENT_CLASS_KEYBOARD
);
342 // Check that we only receive mouse events.
344 SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN
,
345 PP_INPUTEVENT_MOUSEBUTTON_LEFT
)));
347 SimulateInputEvent(CreateWheelEvent()));
349 SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN
,
352 SimulateInputEvent(CreateCharEvent(kSpaceString
)));
357 std::string
TestInputEvent::TestAcceptTouchEvent_1() {
358 // The browser normally sends touch-events to the renderer only if the page
359 // has touch-event handlers. Since test-case.html does not have any
360 // touch-event handler, it would normally not receive any touch events from
361 // the browser. However, if a plugin in the page does accept touch events,
362 // then the browser should start sending touch-events to the page. In this
363 // test, the plugin simply registers for touch-events. The real test is to
364 // verify that the browser knows to send touch-events to the renderer.
365 // If the plugin is removed from the page, then there are no more touch-event
366 // handlers in the page, and browser stops sending touch-events. So to make
367 // it possible to test this properly, the plugin is not removed from the page
368 // at the end of the test.
369 instance_
->set_remove_plugin(false);
370 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
371 PP_INPUTEVENT_CLASS_MOUSE
|
372 PP_INPUTEVENT_CLASS_WHEEL
|
373 PP_INPUTEVENT_CLASS_KEYBOARD
|
374 PP_INPUTEVENT_CLASS_TOUCH
);
378 std::string
TestInputEvent::TestAcceptTouchEvent_2() {
379 // See comment in TestAcceptTouchEvent_1.
380 instance_
->set_remove_plugin(false);
381 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
382 PP_INPUTEVENT_CLASS_MOUSE
|
383 PP_INPUTEVENT_CLASS_WHEEL
|
384 PP_INPUTEVENT_CLASS_KEYBOARD
|
385 PP_INPUTEVENT_CLASS_TOUCH
);
386 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
387 PP_INPUTEVENT_CLASS_TOUCH
);
391 std::string
TestInputEvent::TestAcceptTouchEvent_3() {
392 // See comment in TestAcceptTouchEvent_1.
393 instance_
->set_remove_plugin(false);
394 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
395 PP_INPUTEVENT_CLASS_MOUSE
|
396 PP_INPUTEVENT_CLASS_WHEEL
|
397 PP_INPUTEVENT_CLASS_KEYBOARD
);
398 input_event_interface_
->RequestFilteringInputEvents(instance_
->pp_instance(),
399 PP_INPUTEVENT_CLASS_TOUCH
);
403 std::string
TestInputEvent::TestAcceptTouchEvent_4() {
404 // See comment in TestAcceptTouchEvent_1.
405 instance_
->set_remove_plugin(false);
406 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
407 PP_INPUTEVENT_CLASS_MOUSE
|
408 PP_INPUTEVENT_CLASS_WHEEL
|
409 PP_INPUTEVENT_CLASS_KEYBOARD
);
410 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
411 PP_INPUTEVENT_CLASS_TOUCH
);