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
);
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
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
);
47 TestInputEvent::TestInputEvent(TestingInstance
* 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()),
56 expected_input_event_(0),
57 received_expected_event_(false),
58 received_finish_message_(false) {
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.
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
);
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
));
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();
96 // Set up a listener for our message that signals that all input events have
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
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
"');"
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
);
119 pp::InputEvent
TestInputEvent::CreateMouseEvent(
120 PP_InputEvent_Type type
,
121 PP_InputEvent_MouseButton buttons
) {
122 return pp::MouseInputEvent(
128 GetCenter(view_rect_
),
130 pp::Point()); // movement
133 pp::InputEvent
TestInputEvent::CreateWheelEvent() {
134 return pp::WheelInputEvent(
138 pp::FloatPoint(1, 2),
139 pp::FloatPoint(3, 4),
140 PP_TRUE
); // scroll_by_page
143 pp::InputEvent
TestInputEvent::CreateKeyEvent(PP_InputEvent_Type type
,
145 const std::string
& code
) {
146 return pp::KeyboardInputEvent(
156 pp::InputEvent
TestInputEvent::CreateCharEvent(const std::string
& text
) {
157 return pp::KeyboardInputEvent(
159 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 void TestInputEvent::PostMessageBarrier() {
181 received_finish_message_
= false;
182 instance_
->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE
));
183 testing_interface_
->RunMessageLoop(instance_
->pp_instance());
184 nested_event_
.Wait();
187 // Simulates the input event and calls PostMessage to let us know when
188 // we have received all resulting events from the browser.
189 bool TestInputEvent::SimulateInputEvent(
190 const pp::InputEvent
& input_event
) {
191 expected_input_event_
= pp::InputEvent(input_event
.pp_resource());
192 received_expected_event_
= false;
193 testing_interface_
->SimulateInputEvent(instance_
->pp_instance(),
194 input_event
.pp_resource());
195 PostMessageBarrier();
196 return received_expected_event_
;
199 bool TestInputEvent::AreEquivalentEvents(PP_Resource received
,
200 PP_Resource expected
) {
201 if (!input_event_interface_
->IsInputEvent(received
) ||
202 !input_event_interface_
->IsInputEvent(expected
)) {
206 // Test common fields, except modifiers and time stamp, which may be changed
208 int32_t received_type
= input_event_interface_
->GetType(received
);
209 int32_t expected_type
= input_event_interface_
->GetType(expected
);
210 if (received_type
!= expected_type
) {
211 // Allow key down events to match "raw" key down events.
212 if (expected_type
!= PP_INPUTEVENT_TYPE_KEYDOWN
&&
213 received_type
!= PP_INPUTEVENT_TYPE_RAWKEYDOWN
) {
218 // Test event type-specific fields.
219 switch (input_event_interface_
->GetType(received
)) {
220 case PP_INPUTEVENT_TYPE_MOUSEDOWN
:
221 case PP_INPUTEVENT_TYPE_MOUSEUP
:
222 case PP_INPUTEVENT_TYPE_MOUSEMOVE
:
223 case PP_INPUTEVENT_TYPE_MOUSEENTER
:
224 case PP_INPUTEVENT_TYPE_MOUSELEAVE
:
225 // Check mouse fields, except position and movement, which may be
226 // modified by the renderer.
228 mouse_input_event_interface_
->GetButton(received
) ==
229 mouse_input_event_interface_
->GetButton(expected
) &&
230 mouse_input_event_interface_
->GetClickCount(received
) ==
231 mouse_input_event_interface_
->GetClickCount(expected
);
233 case PP_INPUTEVENT_TYPE_WHEEL
:
235 pp::FloatPoint(wheel_input_event_interface_
->GetDelta(received
)) ==
236 pp::FloatPoint(wheel_input_event_interface_
->GetDelta(expected
)) &&
237 pp::FloatPoint(wheel_input_event_interface_
->GetTicks(received
)) ==
238 pp::FloatPoint(wheel_input_event_interface_
->GetTicks(expected
)) &&
239 wheel_input_event_interface_
->GetScrollByPage(received
) ==
240 wheel_input_event_interface_
->GetScrollByPage(expected
);
242 case PP_INPUTEVENT_TYPE_RAWKEYDOWN
:
243 case PP_INPUTEVENT_TYPE_KEYDOWN
:
244 case PP_INPUTEVENT_TYPE_KEYUP
:
246 keyboard_input_event_interface_
->GetKeyCode(received
) ==
247 keyboard_input_event_interface_
->GetKeyCode(expected
);
249 case PP_INPUTEVENT_TYPE_CHAR
:
251 keyboard_input_event_interface_
->GetKeyCode(received
) ==
252 keyboard_input_event_interface_
->GetKeyCode(expected
) &&
253 pp::Var(pp::PASS_REF
,
254 keyboard_input_event_interface_
->GetCharacterText(received
)) ==
255 pp::Var(pp::PASS_REF
,
256 keyboard_input_event_interface_
->GetCharacterText(expected
));
258 case PP_INPUTEVENT_TYPE_TOUCHSTART
:
259 case PP_INPUTEVENT_TYPE_TOUCHMOVE
:
260 case PP_INPUTEVENT_TYPE_TOUCHEND
:
261 case PP_INPUTEVENT_TYPE_TOUCHCANCEL
: {
262 if (!touch_input_event_interface_
->IsTouchInputEvent(received
) ||
263 !touch_input_event_interface_
->IsTouchInputEvent(expected
))
266 uint32_t touch_count
= touch_input_event_interface_
->GetTouchCount(
267 received
, PP_TOUCHLIST_TYPE_TOUCHES
);
268 if (touch_count
<= 0 ||
269 touch_count
!= touch_input_event_interface_
->GetTouchCount(expected
,
270 PP_TOUCHLIST_TYPE_TOUCHES
))
273 for (uint32_t i
= 0; i
< touch_count
; ++i
) {
274 PP_TouchPoint expected_point
= touch_input_event_interface_
->
275 GetTouchByIndex(expected
, PP_TOUCHLIST_TYPE_TOUCHES
, i
);
276 PP_TouchPoint received_point
= touch_input_event_interface_
->
277 GetTouchByIndex(received
, PP_TOUCHLIST_TYPE_TOUCHES
, i
);
279 if (expected_point
.id
!= received_point
.id
||
280 expected_point
.radius
!= received_point
.radius
||
281 expected_point
.rotation_angle
!= received_point
.rotation_angle
||
282 expected_point
.pressure
!= received_point
.pressure
)
285 if (expected_point
.position
.x
!= received_point
.position
.x
||
286 expected_point
.position
.y
!= received_point
.position
.y
)
299 bool TestInputEvent::HandleInputEvent(const pp::InputEvent
& input_event
) {
300 // Some events may cause extra events to be generated, so look for the
301 // first one that matches.
302 if (!received_expected_event_
) {
303 received_expected_event_
= AreEquivalentEvents(
304 input_event
.pp_resource(),
305 expected_input_event_
.pp_resource());
307 // Handle all input events.
311 void TestInputEvent::HandleMessage(const pp::Var
& message_data
) {
312 if (message_data
.is_string() &&
313 (message_data
.AsString() == FINISHED_WAITING_MESSAGE
)) {
314 testing_interface_
->QuitMessageLoop(instance_
->pp_instance());
315 received_finish_message_
= true;
316 nested_event_
.Signal();
320 void TestInputEvent::DidChangeView(const pp::View
& view
) {
321 view_rect_
= view
.GetRect();
324 std::string
TestInputEvent::TestEvents() {
325 // Request all input event classes.
326 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
327 PP_INPUTEVENT_CLASS_MOUSE
|
328 PP_INPUTEVENT_CLASS_WHEEL
|
329 PP_INPUTEVENT_CLASS_KEYBOARD
|
330 PP_INPUTEVENT_CLASS_TOUCH
);
331 PostMessageBarrier();
333 // Send the events and check that we received them.
335 SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN
,
336 PP_INPUTEVENT_MOUSEBUTTON_LEFT
)));
338 SimulateInputEvent(CreateWheelEvent()));
340 SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN
,
341 kSpaceChar
, kSpaceCode
)));
343 SimulateInputEvent(CreateCharEvent(kSpaceString
)));
344 ASSERT_TRUE(SimulateInputEvent(CreateTouchEvent(PP_INPUTEVENT_TYPE_TOUCHSTART
,
345 pp::FloatPoint(12, 23))));
346 // Request only mouse events.
347 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
348 PP_INPUTEVENT_CLASS_WHEEL
|
349 PP_INPUTEVENT_CLASS_KEYBOARD
);
350 PostMessageBarrier();
352 // Check that we only receive mouse events.
354 SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN
,
355 PP_INPUTEVENT_MOUSEBUTTON_LEFT
)));
357 SimulateInputEvent(CreateWheelEvent()));
359 SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN
,
360 kSpaceChar
, kSpaceCode
)));
362 SimulateInputEvent(CreateCharEvent(kSpaceString
)));
367 std::string
TestInputEvent::TestAcceptTouchEvent_1() {
368 // The browser normally sends touch-events to the renderer only if the page
369 // has touch-event handlers. Since test-case.html does not have any
370 // touch-event handler, it would normally not receive any touch events from
371 // the browser. However, if a plugin in the page does accept touch events,
372 // then the browser should start sending touch-events to the page. In this
373 // test, the plugin simply registers for touch-events. The real test is to
374 // verify that the browser knows to send touch-events to the renderer.
375 // If the plugin is removed from the page, then there are no more touch-event
376 // handlers in the page, and browser stops sending touch-events. So to make
377 // it possible to test this properly, the plugin is not removed from the page
378 // at the end of the test.
379 instance_
->set_remove_plugin(false);
380 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
381 PP_INPUTEVENT_CLASS_MOUSE
|
382 PP_INPUTEVENT_CLASS_WHEEL
|
383 PP_INPUTEVENT_CLASS_KEYBOARD
|
384 PP_INPUTEVENT_CLASS_TOUCH
);
388 std::string
TestInputEvent::TestAcceptTouchEvent_2() {
389 // See comment in TestAcceptTouchEvent_1.
390 instance_
->set_remove_plugin(false);
391 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
392 PP_INPUTEVENT_CLASS_MOUSE
|
393 PP_INPUTEVENT_CLASS_WHEEL
|
394 PP_INPUTEVENT_CLASS_KEYBOARD
|
395 PP_INPUTEVENT_CLASS_TOUCH
);
396 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
397 PP_INPUTEVENT_CLASS_TOUCH
);
401 std::string
TestInputEvent::TestAcceptTouchEvent_3() {
402 // See comment in TestAcceptTouchEvent_1.
403 instance_
->set_remove_plugin(false);
404 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
405 PP_INPUTEVENT_CLASS_MOUSE
|
406 PP_INPUTEVENT_CLASS_WHEEL
|
407 PP_INPUTEVENT_CLASS_KEYBOARD
);
408 input_event_interface_
->RequestFilteringInputEvents(instance_
->pp_instance(),
409 PP_INPUTEVENT_CLASS_TOUCH
);
413 std::string
TestInputEvent::TestAcceptTouchEvent_4() {
414 // See comment in TestAcceptTouchEvent_1.
415 instance_
->set_remove_plugin(false);
416 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
417 PP_INPUTEVENT_CLASS_MOUSE
|
418 PP_INPUTEVENT_CLASS_WHEEL
|
419 PP_INPUTEVENT_CLASS_KEYBOARD
);
420 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
421 PP_INPUTEVENT_CLASS_TOUCH
);