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
),
55 expected_input_event_(0),
56 received_expected_event_(false),
57 received_finish_message_(false) {
60 TestInputEvent::~TestInputEvent() {
61 // Remove the special listener that only responds to a
62 // FINISHED_WAITING_MESSAGE string. See Init for where it gets added.
64 js_code
+= "var plugin = document.getElementById('plugin');"
65 "plugin.removeEventListener('message',"
66 " plugin.wait_for_messages_handler);"
67 "delete plugin.wait_for_messages_handler;";
68 instance_
->EvalScript(js_code
);
71 bool TestInputEvent::Init() {
72 input_event_interface_
= static_cast<const PPB_InputEvent
*>(
73 pp::Module::Get()->GetBrowserInterface(PPB_INPUT_EVENT_INTERFACE
));
74 mouse_input_event_interface_
= static_cast<const PPB_MouseInputEvent
*>(
75 pp::Module::Get()->GetBrowserInterface(
76 PPB_MOUSE_INPUT_EVENT_INTERFACE
));
77 wheel_input_event_interface_
= static_cast<const PPB_WheelInputEvent
*>(
78 pp::Module::Get()->GetBrowserInterface(
79 PPB_WHEEL_INPUT_EVENT_INTERFACE
));
80 keyboard_input_event_interface_
= static_cast<const PPB_KeyboardInputEvent
*>(
81 pp::Module::Get()->GetBrowserInterface(
82 PPB_KEYBOARD_INPUT_EVENT_INTERFACE
));
83 touch_input_event_interface_
= static_cast<const PPB_TouchInputEvent
*>(
84 pp::Module::Get()->GetBrowserInterface(
85 PPB_TOUCH_INPUT_EVENT_INTERFACE
));
88 input_event_interface_
&&
89 mouse_input_event_interface_
&&
90 wheel_input_event_interface_
&&
91 keyboard_input_event_interface_
&&
92 touch_input_event_interface_
&&
93 CheckTestingInterface();
95 // Set up a listener for our message that signals that all input events have
98 // Note the following code is dependent on some features of test_case.html.
99 // E.g., it is assumed that the DOM element where the plugin is embedded has
100 // an id of 'plugin', and there is a function 'IsTestingMessage' that allows
101 // us to ignore the messages that are intended for use by the testing
103 js_code
+= "var plugin = document.getElementById('plugin');"
104 "var wait_for_messages_handler = function(message_event) {"
105 " if (!IsTestingMessage(message_event.data) &&"
106 " message_event.data === '" FINISHED_WAITING_MESSAGE
"') {"
107 " plugin.postMessage('" FINISHED_WAITING_MESSAGE
"');"
110 "plugin.addEventListener('message', wait_for_messages_handler);"
111 // Stash it on the plugin so we can remove it in the destructor.
112 "plugin.wait_for_messages_handler = wait_for_messages_handler;";
113 instance_
->EvalScript(js_code
);
118 pp::InputEvent
TestInputEvent::CreateMouseEvent(
119 PP_InputEvent_Type type
,
120 PP_InputEvent_MouseButton buttons
) {
121 return pp::MouseInputEvent(
127 GetCenter(view_rect_
),
129 pp::Point()); // movement
132 pp::InputEvent
TestInputEvent::CreateWheelEvent() {
133 return pp::WheelInputEvent(
137 pp::FloatPoint(1, 2),
138 pp::FloatPoint(3, 4),
139 PP_TRUE
); // scroll_by_page
142 pp::InputEvent
TestInputEvent::CreateKeyEvent(PP_InputEvent_Type type
,
144 return pp::KeyboardInputEvent(
153 pp::InputEvent
TestInputEvent::CreateCharEvent(const std::string
& text
) {
154 return pp::KeyboardInputEvent(
156 PP_INPUTEVENT_TYPE_CHAR
,
163 pp::InputEvent
TestInputEvent::CreateTouchEvent(PP_InputEvent_Type type
,
164 const pp::FloatPoint
& point
) {
165 PP_TouchPoint touch_point
= PP_MakeTouchPoint();
166 touch_point
.position
= point
;
168 pp::TouchInputEvent
touch_event(instance_
, type
, 100, 0);
169 touch_event
.AddTouchPoint(PP_TOUCHLIST_TYPE_TOUCHES
, touch_point
);
170 touch_event
.AddTouchPoint(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES
, touch_point
);
171 touch_event
.AddTouchPoint(PP_TOUCHLIST_TYPE_TARGETTOUCHES
, touch_point
);
176 // Simulates the input event and calls PostMessage to let us know when
177 // we have received all resulting events from the browser.
178 bool TestInputEvent::SimulateInputEvent(
179 const pp::InputEvent
& input_event
) {
180 expected_input_event_
= pp::InputEvent(input_event
.pp_resource());
181 received_expected_event_
= false;
182 received_finish_message_
= false;
183 testing_interface_
->SimulateInputEvent(instance_
->pp_instance(),
184 input_event
.pp_resource());
185 instance_
->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE
));
186 testing_interface_
->RunMessageLoop(instance_
->pp_instance());
187 return received_finish_message_
&& received_expected_event_
;
190 bool TestInputEvent::AreEquivalentEvents(PP_Resource received
,
191 PP_Resource expected
) {
192 if (!input_event_interface_
->IsInputEvent(received
) ||
193 !input_event_interface_
->IsInputEvent(expected
)) {
197 // Test common fields, except modifiers and time stamp, which may be changed
199 int32_t received_type
= input_event_interface_
->GetType(received
);
200 int32_t expected_type
= input_event_interface_
->GetType(expected
);
201 if (received_type
!= expected_type
) {
202 // Allow key down events to match "raw" key down events.
203 if (expected_type
!= PP_INPUTEVENT_TYPE_KEYDOWN
&&
204 received_type
!= PP_INPUTEVENT_TYPE_RAWKEYDOWN
) {
209 // Test event type-specific fields.
210 switch (input_event_interface_
->GetType(received
)) {
211 case PP_INPUTEVENT_TYPE_MOUSEDOWN
:
212 case PP_INPUTEVENT_TYPE_MOUSEUP
:
213 case PP_INPUTEVENT_TYPE_MOUSEMOVE
:
214 case PP_INPUTEVENT_TYPE_MOUSEENTER
:
215 case PP_INPUTEVENT_TYPE_MOUSELEAVE
:
216 // Check mouse fields, except position and movement, which may be
217 // modified by the renderer.
219 mouse_input_event_interface_
->GetButton(received
) ==
220 mouse_input_event_interface_
->GetButton(expected
) &&
221 mouse_input_event_interface_
->GetClickCount(received
) ==
222 mouse_input_event_interface_
->GetClickCount(expected
);
224 case PP_INPUTEVENT_TYPE_WHEEL
:
226 pp::FloatPoint(wheel_input_event_interface_
->GetDelta(received
)) ==
227 pp::FloatPoint(wheel_input_event_interface_
->GetDelta(expected
)) &&
228 pp::FloatPoint(wheel_input_event_interface_
->GetTicks(received
)) ==
229 pp::FloatPoint(wheel_input_event_interface_
->GetTicks(expected
)) &&
230 wheel_input_event_interface_
->GetScrollByPage(received
) ==
231 wheel_input_event_interface_
->GetScrollByPage(expected
);
233 case PP_INPUTEVENT_TYPE_RAWKEYDOWN
:
234 case PP_INPUTEVENT_TYPE_KEYDOWN
:
235 case PP_INPUTEVENT_TYPE_KEYUP
:
237 keyboard_input_event_interface_
->GetKeyCode(received
) ==
238 keyboard_input_event_interface_
->GetKeyCode(expected
);
240 case PP_INPUTEVENT_TYPE_CHAR
:
242 keyboard_input_event_interface_
->GetKeyCode(received
) ==
243 keyboard_input_event_interface_
->GetKeyCode(expected
) &&
244 pp::Var(pp::PASS_REF
,
245 keyboard_input_event_interface_
->GetCharacterText(received
)) ==
246 pp::Var(pp::PASS_REF
,
247 keyboard_input_event_interface_
->GetCharacterText(expected
));
249 case PP_INPUTEVENT_TYPE_TOUCHSTART
:
250 case PP_INPUTEVENT_TYPE_TOUCHMOVE
:
251 case PP_INPUTEVENT_TYPE_TOUCHEND
:
252 case PP_INPUTEVENT_TYPE_TOUCHCANCEL
: {
253 if (!touch_input_event_interface_
->IsTouchInputEvent(received
) ||
254 !touch_input_event_interface_
->IsTouchInputEvent(expected
))
257 uint32_t touch_count
= touch_input_event_interface_
->GetTouchCount(
258 received
, PP_TOUCHLIST_TYPE_TOUCHES
);
259 if (touch_count
<= 0 ||
260 touch_count
!= touch_input_event_interface_
->GetTouchCount(expected
,
261 PP_TOUCHLIST_TYPE_TOUCHES
))
264 for (uint32_t i
= 0; i
< touch_count
; ++i
) {
265 PP_TouchPoint expected_point
= touch_input_event_interface_
->
266 GetTouchByIndex(expected
, PP_TOUCHLIST_TYPE_TOUCHES
, i
);
267 PP_TouchPoint received_point
= touch_input_event_interface_
->
268 GetTouchByIndex(received
, PP_TOUCHLIST_TYPE_TOUCHES
, i
);
270 if (expected_point
.id
!= received_point
.id
||
271 expected_point
.radius
!= received_point
.radius
||
272 expected_point
.rotation_angle
!= received_point
.rotation_angle
||
273 expected_point
.pressure
!= received_point
.pressure
)
276 if (expected_point
.position
.x
!= received_point
.position
.x
||
277 expected_point
.position
.y
!= received_point
.position
.y
)
290 bool TestInputEvent::HandleInputEvent(const pp::InputEvent
& input_event
) {
291 // Some events may cause extra events to be generated, so look for the
292 // first one that matches.
293 if (!received_expected_event_
) {
294 received_expected_event_
= AreEquivalentEvents(
295 input_event
.pp_resource(),
296 expected_input_event_
.pp_resource());
298 // Handle all input events.
302 void TestInputEvent::HandleMessage(const pp::Var
& message_data
) {
303 if (message_data
.is_string() &&
304 (message_data
.AsString() == FINISHED_WAITING_MESSAGE
)) {
305 testing_interface_
->QuitMessageLoop(instance_
->pp_instance());
306 received_finish_message_
= true;
310 void TestInputEvent::DidChangeView(const pp::View
& view
) {
311 view_rect_
= view
.GetRect();
314 std::string
TestInputEvent::TestEvents() {
315 // Request all input event classes.
316 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
317 PP_INPUTEVENT_CLASS_MOUSE
|
318 PP_INPUTEVENT_CLASS_WHEEL
|
319 PP_INPUTEVENT_CLASS_KEYBOARD
|
320 PP_INPUTEVENT_CLASS_TOUCH
);
321 // Send the events and check that we received them.
323 SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN
,
324 PP_INPUTEVENT_MOUSEBUTTON_LEFT
)));
326 SimulateInputEvent(CreateWheelEvent()));
328 SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN
,
331 SimulateInputEvent(CreateCharEvent(kSpaceString
)));
332 ASSERT_TRUE(SimulateInputEvent(CreateTouchEvent(PP_INPUTEVENT_TYPE_TOUCHSTART
,
333 pp::FloatPoint(12, 23))));
334 // Request only mouse events.
335 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
336 PP_INPUTEVENT_CLASS_WHEEL
|
337 PP_INPUTEVENT_CLASS_KEYBOARD
);
338 // Check that we only receive mouse events.
340 SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN
,
341 PP_INPUTEVENT_MOUSEBUTTON_LEFT
)));
343 SimulateInputEvent(CreateWheelEvent()));
345 SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN
,
348 SimulateInputEvent(CreateCharEvent(kSpaceString
)));
353 std::string
TestInputEvent::TestAcceptTouchEvent_1() {
354 // The browser normally sends touch-events to the renderer only if the page
355 // has touch-event handlers. Since test-case.html does not have any
356 // touch-event handler, it would normally not receive any touch events from
357 // the browser. However, if a plugin in the page does accept touch events,
358 // then the browser should start sending touch-events to the page. In this
359 // test, the plugin simply registers for touch-events. The real test is to
360 // verify that the browser knows to send touch-events to the renderer.
361 // If the plugin is removed from the page, then there are no more touch-event
362 // handlers in the page, and browser stops sending touch-events. So to make
363 // it possible to test this properly, the plugin is not removed from the page
364 // at the end of the test.
365 instance_
->set_remove_plugin(false);
366 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
367 PP_INPUTEVENT_CLASS_MOUSE
|
368 PP_INPUTEVENT_CLASS_WHEEL
|
369 PP_INPUTEVENT_CLASS_KEYBOARD
|
370 PP_INPUTEVENT_CLASS_TOUCH
);
374 std::string
TestInputEvent::TestAcceptTouchEvent_2() {
375 // See comment in TestAcceptTouchEvent_1.
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
);
382 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
383 PP_INPUTEVENT_CLASS_TOUCH
);
387 std::string
TestInputEvent::TestAcceptTouchEvent_3() {
388 // See comment in TestAcceptTouchEvent_1.
389 instance_
->set_remove_plugin(false);
390 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
391 PP_INPUTEVENT_CLASS_MOUSE
|
392 PP_INPUTEVENT_CLASS_WHEEL
|
393 PP_INPUTEVENT_CLASS_KEYBOARD
);
394 input_event_interface_
->RequestFilteringInputEvents(instance_
->pp_instance(),
395 PP_INPUTEVENT_CLASS_TOUCH
);
399 std::string
TestInputEvent::TestAcceptTouchEvent_4() {
400 // See comment in TestAcceptTouchEvent_1.
401 instance_
->set_remove_plugin(false);
402 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
403 PP_INPUTEVENT_CLASS_MOUSE
|
404 PP_INPUTEVENT_CLASS_WHEEL
|
405 PP_INPUTEVENT_CLASS_KEYBOARD
);
406 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
407 PP_INPUTEVENT_CLASS_TOUCH
);