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 // 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 return pp::KeyboardInputEvent(
154 pp::InputEvent
TestInputEvent::CreateCharEvent(const std::string
& text
) {
155 return pp::KeyboardInputEvent(
157 PP_INPUTEVENT_TYPE_CHAR
,
164 pp::InputEvent
TestInputEvent::CreateTouchEvent(PP_InputEvent_Type type
,
165 const pp::FloatPoint
& point
) {
166 PP_TouchPoint touch_point
= PP_MakeTouchPoint();
167 touch_point
.position
= point
;
169 pp::TouchInputEvent
touch_event(instance_
, type
, 100, 0);
170 touch_event
.AddTouchPoint(PP_TOUCHLIST_TYPE_TOUCHES
, touch_point
);
171 touch_event
.AddTouchPoint(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES
, touch_point
);
172 touch_event
.AddTouchPoint(PP_TOUCHLIST_TYPE_TARGETTOUCHES
, touch_point
);
177 void TestInputEvent::PostMessageBarrier() {
178 received_finish_message_
= false;
179 instance_
->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE
));
180 testing_interface_
->RunMessageLoop(instance_
->pp_instance());
181 nested_event_
.Wait();
184 // Simulates the input event and calls PostMessage to let us know when
185 // we have received all resulting events from the browser.
186 bool TestInputEvent::SimulateInputEvent(
187 const pp::InputEvent
& input_event
) {
188 expected_input_event_
= pp::InputEvent(input_event
.pp_resource());
189 received_expected_event_
= false;
190 testing_interface_
->SimulateInputEvent(instance_
->pp_instance(),
191 input_event
.pp_resource());
192 PostMessageBarrier();
193 return received_expected_event_
;
196 bool TestInputEvent::AreEquivalentEvents(PP_Resource received
,
197 PP_Resource expected
) {
198 if (!input_event_interface_
->IsInputEvent(received
) ||
199 !input_event_interface_
->IsInputEvent(expected
)) {
203 // Test common fields, except modifiers and time stamp, which may be changed
205 int32_t received_type
= input_event_interface_
->GetType(received
);
206 int32_t expected_type
= input_event_interface_
->GetType(expected
);
207 if (received_type
!= expected_type
) {
208 // Allow key down events to match "raw" key down events.
209 if (expected_type
!= PP_INPUTEVENT_TYPE_KEYDOWN
&&
210 received_type
!= PP_INPUTEVENT_TYPE_RAWKEYDOWN
) {
215 // Test event type-specific fields.
216 switch (input_event_interface_
->GetType(received
)) {
217 case PP_INPUTEVENT_TYPE_MOUSEDOWN
:
218 case PP_INPUTEVENT_TYPE_MOUSEUP
:
219 case PP_INPUTEVENT_TYPE_MOUSEMOVE
:
220 case PP_INPUTEVENT_TYPE_MOUSEENTER
:
221 case PP_INPUTEVENT_TYPE_MOUSELEAVE
:
222 // Check mouse fields, except position and movement, which may be
223 // modified by the renderer.
225 mouse_input_event_interface_
->GetButton(received
) ==
226 mouse_input_event_interface_
->GetButton(expected
) &&
227 mouse_input_event_interface_
->GetClickCount(received
) ==
228 mouse_input_event_interface_
->GetClickCount(expected
);
230 case PP_INPUTEVENT_TYPE_WHEEL
:
232 pp::FloatPoint(wheel_input_event_interface_
->GetDelta(received
)) ==
233 pp::FloatPoint(wheel_input_event_interface_
->GetDelta(expected
)) &&
234 pp::FloatPoint(wheel_input_event_interface_
->GetTicks(received
)) ==
235 pp::FloatPoint(wheel_input_event_interface_
->GetTicks(expected
)) &&
236 wheel_input_event_interface_
->GetScrollByPage(received
) ==
237 wheel_input_event_interface_
->GetScrollByPage(expected
);
239 case PP_INPUTEVENT_TYPE_RAWKEYDOWN
:
240 case PP_INPUTEVENT_TYPE_KEYDOWN
:
241 case PP_INPUTEVENT_TYPE_KEYUP
:
243 keyboard_input_event_interface_
->GetKeyCode(received
) ==
244 keyboard_input_event_interface_
->GetKeyCode(expected
);
246 case PP_INPUTEVENT_TYPE_CHAR
:
248 keyboard_input_event_interface_
->GetKeyCode(received
) ==
249 keyboard_input_event_interface_
->GetKeyCode(expected
) &&
250 pp::Var(pp::PASS_REF
,
251 keyboard_input_event_interface_
->GetCharacterText(received
)) ==
252 pp::Var(pp::PASS_REF
,
253 keyboard_input_event_interface_
->GetCharacterText(expected
));
255 case PP_INPUTEVENT_TYPE_TOUCHSTART
:
256 case PP_INPUTEVENT_TYPE_TOUCHMOVE
:
257 case PP_INPUTEVENT_TYPE_TOUCHEND
:
258 case PP_INPUTEVENT_TYPE_TOUCHCANCEL
: {
259 if (!touch_input_event_interface_
->IsTouchInputEvent(received
) ||
260 !touch_input_event_interface_
->IsTouchInputEvent(expected
))
263 uint32_t touch_count
= touch_input_event_interface_
->GetTouchCount(
264 received
, PP_TOUCHLIST_TYPE_TOUCHES
);
265 if (touch_count
<= 0 ||
266 touch_count
!= touch_input_event_interface_
->GetTouchCount(expected
,
267 PP_TOUCHLIST_TYPE_TOUCHES
))
270 for (uint32_t i
= 0; i
< touch_count
; ++i
) {
271 PP_TouchPoint expected_point
= touch_input_event_interface_
->
272 GetTouchByIndex(expected
, PP_TOUCHLIST_TYPE_TOUCHES
, i
);
273 PP_TouchPoint received_point
= touch_input_event_interface_
->
274 GetTouchByIndex(received
, PP_TOUCHLIST_TYPE_TOUCHES
, i
);
276 if (expected_point
.id
!= received_point
.id
||
277 expected_point
.radius
!= received_point
.radius
||
278 expected_point
.rotation_angle
!= received_point
.rotation_angle
||
279 expected_point
.pressure
!= received_point
.pressure
)
282 if (expected_point
.position
.x
!= received_point
.position
.x
||
283 expected_point
.position
.y
!= received_point
.position
.y
)
296 bool TestInputEvent::HandleInputEvent(const pp::InputEvent
& input_event
) {
297 // Some events may cause extra events to be generated, so look for the
298 // first one that matches.
299 if (!received_expected_event_
) {
300 received_expected_event_
= AreEquivalentEvents(
301 input_event
.pp_resource(),
302 expected_input_event_
.pp_resource());
304 // Handle all input events.
308 void TestInputEvent::HandleMessage(const pp::Var
& message_data
) {
309 if (message_data
.is_string() &&
310 (message_data
.AsString() == FINISHED_WAITING_MESSAGE
)) {
311 testing_interface_
->QuitMessageLoop(instance_
->pp_instance());
312 received_finish_message_
= true;
313 nested_event_
.Signal();
317 void TestInputEvent::DidChangeView(const pp::View
& view
) {
318 view_rect_
= view
.GetRect();
321 std::string
TestInputEvent::TestEvents() {
322 // Request all input event classes.
323 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
324 PP_INPUTEVENT_CLASS_MOUSE
|
325 PP_INPUTEVENT_CLASS_WHEEL
|
326 PP_INPUTEVENT_CLASS_KEYBOARD
|
327 PP_INPUTEVENT_CLASS_TOUCH
);
328 PostMessageBarrier();
330 // Send the events and check that we received them.
332 SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN
,
333 PP_INPUTEVENT_MOUSEBUTTON_LEFT
)));
335 SimulateInputEvent(CreateWheelEvent()));
337 SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN
,
340 SimulateInputEvent(CreateCharEvent(kSpaceString
)));
341 ASSERT_TRUE(SimulateInputEvent(CreateTouchEvent(PP_INPUTEVENT_TYPE_TOUCHSTART
,
342 pp::FloatPoint(12, 23))));
343 // Request only mouse events.
344 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
345 PP_INPUTEVENT_CLASS_WHEEL
|
346 PP_INPUTEVENT_CLASS_KEYBOARD
);
347 PostMessageBarrier();
349 // Check that we only receive mouse events.
351 SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN
,
352 PP_INPUTEVENT_MOUSEBUTTON_LEFT
)));
354 SimulateInputEvent(CreateWheelEvent()));
356 SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN
,
359 SimulateInputEvent(CreateCharEvent(kSpaceString
)));
364 std::string
TestInputEvent::TestAcceptTouchEvent_1() {
365 // The browser normally sends touch-events to the renderer only if the page
366 // has touch-event handlers. Since test-case.html does not have any
367 // touch-event handler, it would normally not receive any touch events from
368 // the browser. However, if a plugin in the page does accept touch events,
369 // then the browser should start sending touch-events to the page. In this
370 // test, the plugin simply registers for touch-events. The real test is to
371 // verify that the browser knows to send touch-events to the renderer.
372 // If the plugin is removed from the page, then there are no more touch-event
373 // handlers in the page, and browser stops sending touch-events. So to make
374 // it possible to test this properly, the plugin is not removed from the page
375 // at the end of the test.
376 instance_
->set_remove_plugin(false);
377 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
378 PP_INPUTEVENT_CLASS_MOUSE
|
379 PP_INPUTEVENT_CLASS_WHEEL
|
380 PP_INPUTEVENT_CLASS_KEYBOARD
|
381 PP_INPUTEVENT_CLASS_TOUCH
);
385 std::string
TestInputEvent::TestAcceptTouchEvent_2() {
386 // See comment in TestAcceptTouchEvent_1.
387 instance_
->set_remove_plugin(false);
388 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
389 PP_INPUTEVENT_CLASS_MOUSE
|
390 PP_INPUTEVENT_CLASS_WHEEL
|
391 PP_INPUTEVENT_CLASS_KEYBOARD
|
392 PP_INPUTEVENT_CLASS_TOUCH
);
393 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
394 PP_INPUTEVENT_CLASS_TOUCH
);
398 std::string
TestInputEvent::TestAcceptTouchEvent_3() {
399 // See comment in TestAcceptTouchEvent_1.
400 instance_
->set_remove_plugin(false);
401 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
402 PP_INPUTEVENT_CLASS_MOUSE
|
403 PP_INPUTEVENT_CLASS_WHEEL
|
404 PP_INPUTEVENT_CLASS_KEYBOARD
);
405 input_event_interface_
->RequestFilteringInputEvents(instance_
->pp_instance(),
406 PP_INPUTEVENT_CLASS_TOUCH
);
410 std::string
TestInputEvent::TestAcceptTouchEvent_4() {
411 // See comment in TestAcceptTouchEvent_1.
412 instance_
->set_remove_plugin(false);
413 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
414 PP_INPUTEVENT_CLASS_MOUSE
|
415 PP_INPUTEVENT_CLASS_WHEEL
|
416 PP_INPUTEVENT_CLASS_KEYBOARD
);
417 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
418 PP_INPUTEVENT_CLASS_TOUCH
);