• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ppapi/tests/test_input_event.h"
6 
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"
13 
14 REGISTER_TEST_CASE(InputEvent);
15 
16 namespace {
17 
18 const uint32_t kSpaceChar = 0x20;
19 const char* kSpaceString = " ";
20 
21 #define FINISHED_WAITING_MESSAGE "TEST_INPUT_EVENT_FINISHED_WAITING"
22 
GetCenter(const pp::Rect & rect)23 pp::Point GetCenter(const pp::Rect& rect) {
24   return pp::Point(
25       rect.x() + rect.width() / 2,
26       rect.y() + rect.height() / 2);
27 }
28 
29 }  // namespace
30 
RunTests(const std::string & filter)31 void TestInputEvent::RunTests(const std::string& filter) {
32   RUN_TEST(Events, filter);
33 
34   // The AcceptTouchEvent_N tests should not be run when the filter is empty;
35   // they can only be run one at a time.
36   // TODO(dmichael): Figure out a way to make these run in the same test fixture
37   //                 instance.
38   if (!ShouldRunAllTests(filter)) {
39     RUN_TEST(AcceptTouchEvent_1, filter);
40     RUN_TEST(AcceptTouchEvent_2, filter);
41     RUN_TEST(AcceptTouchEvent_3, filter);
42     RUN_TEST(AcceptTouchEvent_4, filter);
43   }
44 }
45 
TestInputEvent(TestingInstance * instance)46 TestInputEvent::TestInputEvent(TestingInstance* instance)
47     : TestCase(instance),
48       input_event_interface_(NULL),
49       mouse_input_event_interface_(NULL),
50       wheel_input_event_interface_(NULL),
51       keyboard_input_event_interface_(NULL),
52       touch_input_event_interface_(NULL),
53       nested_event_(instance->pp_instance()),
54       view_rect_(),
55       expected_input_event_(0),
56       received_expected_event_(false),
57       received_finish_message_(false) {
58 }
59 
~TestInputEvent()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.
63   std::string js_code;
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);
69 }
70 
Init()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));
86 
87   bool success =
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();
94 
95   // Set up a listener for our message that signals that all input events have
96   // been received.
97   std::string js_code;
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
102   // framework itself.
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 "');"
108              "  }"
109              "};"
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);
114 
115   return success;
116 }
117 
CreateMouseEvent(PP_InputEvent_Type type,PP_InputEvent_MouseButton buttons)118 pp::InputEvent TestInputEvent::CreateMouseEvent(
119     PP_InputEvent_Type type,
120     PP_InputEvent_MouseButton buttons) {
121   return pp::MouseInputEvent(
122       instance_,
123       type,
124       100,  // time_stamp
125       0,  // modifiers
126       buttons,
127       GetCenter(view_rect_),
128       1,  // click count
129       pp::Point());  // movement
130 }
131 
CreateWheelEvent()132 pp::InputEvent TestInputEvent::CreateWheelEvent() {
133   return pp::WheelInputEvent(
134       instance_,
135       100,  // time_stamp
136       0,  // modifiers
137       pp::FloatPoint(1, 2),
138       pp::FloatPoint(3, 4),
139       PP_TRUE);  // scroll_by_page
140 }
141 
CreateKeyEvent(PP_InputEvent_Type type,uint32_t key_code)142 pp::InputEvent TestInputEvent::CreateKeyEvent(PP_InputEvent_Type type,
143                                               uint32_t key_code) {
144   return pp::KeyboardInputEvent(
145       instance_,
146       type,
147       100,  // time_stamp
148       0,  // modifiers
149       key_code,
150       pp::Var());
151 }
152 
CreateCharEvent(const std::string & text)153 pp::InputEvent TestInputEvent::CreateCharEvent(const std::string& text) {
154   return pp::KeyboardInputEvent(
155       instance_,
156       PP_INPUTEVENT_TYPE_CHAR,
157       100,  // time_stamp
158       0,  // modifiers
159       0,  // keycode
160       pp::Var(text));
161 }
162 
CreateTouchEvent(PP_InputEvent_Type type,const pp::FloatPoint & point)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;
167 
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);
172 
173   return touch_event;
174 }
175 
PostMessageBarrier()176 void TestInputEvent::PostMessageBarrier() {
177   received_finish_message_ = false;
178   instance_->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE));
179   testing_interface_->RunMessageLoop(instance_->pp_instance());
180   nested_event_.Wait();
181 }
182 
183 // Simulates the input event and calls PostMessage to let us know when
184 // we have received all resulting events from the browser.
SimulateInputEvent(const pp::InputEvent & input_event)185 bool TestInputEvent::SimulateInputEvent(
186     const pp::InputEvent& input_event) {
187   expected_input_event_ = pp::InputEvent(input_event.pp_resource());
188   received_expected_event_ = false;
189   testing_interface_->SimulateInputEvent(instance_->pp_instance(),
190                                          input_event.pp_resource());
191   PostMessageBarrier();
192   return received_expected_event_;
193 }
194 
AreEquivalentEvents(PP_Resource received,PP_Resource expected)195 bool TestInputEvent::AreEquivalentEvents(PP_Resource received,
196                                          PP_Resource expected) {
197   if (!input_event_interface_->IsInputEvent(received) ||
198       !input_event_interface_->IsInputEvent(expected)) {
199     return false;
200   }
201 
202   // Test common fields, except modifiers and time stamp, which may be changed
203   // by the browser.
204   int32_t received_type = input_event_interface_->GetType(received);
205   int32_t expected_type = input_event_interface_->GetType(expected);
206   if (received_type != expected_type) {
207     // Allow key down events to match "raw" key down events.
208     if (expected_type != PP_INPUTEVENT_TYPE_KEYDOWN &&
209         received_type != PP_INPUTEVENT_TYPE_RAWKEYDOWN) {
210       return false;
211     }
212   }
213 
214   // Test event type-specific fields.
215   switch (input_event_interface_->GetType(received)) {
216     case PP_INPUTEVENT_TYPE_MOUSEDOWN:
217     case PP_INPUTEVENT_TYPE_MOUSEUP:
218     case PP_INPUTEVENT_TYPE_MOUSEMOVE:
219     case PP_INPUTEVENT_TYPE_MOUSEENTER:
220     case PP_INPUTEVENT_TYPE_MOUSELEAVE:
221       // Check mouse fields, except position and movement, which may be
222       // modified by the renderer.
223       return
224           mouse_input_event_interface_->GetButton(received) ==
225           mouse_input_event_interface_->GetButton(expected) &&
226           mouse_input_event_interface_->GetClickCount(received) ==
227           mouse_input_event_interface_->GetClickCount(expected);
228 
229     case PP_INPUTEVENT_TYPE_WHEEL:
230       return
231           pp::FloatPoint(wheel_input_event_interface_->GetDelta(received)) ==
232           pp::FloatPoint(wheel_input_event_interface_->GetDelta(expected)) &&
233           pp::FloatPoint(wheel_input_event_interface_->GetTicks(received)) ==
234           pp::FloatPoint(wheel_input_event_interface_->GetTicks(expected)) &&
235           wheel_input_event_interface_->GetScrollByPage(received) ==
236           wheel_input_event_interface_->GetScrollByPage(expected);
237 
238     case PP_INPUTEVENT_TYPE_RAWKEYDOWN:
239     case PP_INPUTEVENT_TYPE_KEYDOWN:
240     case PP_INPUTEVENT_TYPE_KEYUP:
241       return
242           keyboard_input_event_interface_->GetKeyCode(received) ==
243           keyboard_input_event_interface_->GetKeyCode(expected);
244 
245     case PP_INPUTEVENT_TYPE_CHAR:
246       return
247           keyboard_input_event_interface_->GetKeyCode(received) ==
248           keyboard_input_event_interface_->GetKeyCode(expected) &&
249           pp::Var(pp::PASS_REF,
250               keyboard_input_event_interface_->GetCharacterText(received)) ==
251           pp::Var(pp::PASS_REF,
252               keyboard_input_event_interface_->GetCharacterText(expected));
253 
254     case PP_INPUTEVENT_TYPE_TOUCHSTART:
255     case PP_INPUTEVENT_TYPE_TOUCHMOVE:
256     case PP_INPUTEVENT_TYPE_TOUCHEND:
257     case PP_INPUTEVENT_TYPE_TOUCHCANCEL: {
258       if (!touch_input_event_interface_->IsTouchInputEvent(received) ||
259           !touch_input_event_interface_->IsTouchInputEvent(expected))
260         return false;
261 
262       uint32_t touch_count = touch_input_event_interface_->GetTouchCount(
263           received, PP_TOUCHLIST_TYPE_TOUCHES);
264       if (touch_count <= 0 ||
265           touch_count != touch_input_event_interface_->GetTouchCount(expected,
266               PP_TOUCHLIST_TYPE_TOUCHES))
267         return false;
268 
269       for (uint32_t i = 0; i < touch_count; ++i) {
270         PP_TouchPoint expected_point = touch_input_event_interface_->
271             GetTouchByIndex(expected, PP_TOUCHLIST_TYPE_TOUCHES, i);
272         PP_TouchPoint received_point = touch_input_event_interface_->
273             GetTouchByIndex(received, PP_TOUCHLIST_TYPE_TOUCHES, i);
274 
275         if (expected_point.id != received_point.id ||
276             expected_point.radius != received_point.radius ||
277             expected_point.rotation_angle != received_point.rotation_angle ||
278             expected_point.pressure != received_point.pressure)
279           return false;
280 
281         if (expected_point.position.x != received_point.position.x ||
282             expected_point.position.y != received_point.position.y)
283           return false;
284       }
285       return true;
286     }
287 
288     default:
289       break;
290   }
291 
292   return false;
293 }
294 
HandleInputEvent(const pp::InputEvent & input_event)295 bool TestInputEvent::HandleInputEvent(const pp::InputEvent& input_event) {
296   // Some events may cause extra events to be generated, so look for the
297   // first one that matches.
298   if (!received_expected_event_) {
299     received_expected_event_ = AreEquivalentEvents(
300         input_event.pp_resource(),
301         expected_input_event_.pp_resource());
302   }
303   // Handle all input events.
304   return true;
305 }
306 
HandleMessage(const pp::Var & message_data)307 void TestInputEvent::HandleMessage(const pp::Var& message_data) {
308   if (message_data.is_string() &&
309       (message_data.AsString() == FINISHED_WAITING_MESSAGE)) {
310     testing_interface_->QuitMessageLoop(instance_->pp_instance());
311     received_finish_message_ = true;
312     nested_event_.Signal();
313   }
314 }
315 
DidChangeView(const pp::View & view)316 void TestInputEvent::DidChangeView(const pp::View& view) {
317   view_rect_ = view.GetRect();
318 }
319 
TestEvents()320 std::string TestInputEvent::TestEvents() {
321   // Request all input event classes.
322   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
323                                              PP_INPUTEVENT_CLASS_MOUSE |
324                                              PP_INPUTEVENT_CLASS_WHEEL |
325                                              PP_INPUTEVENT_CLASS_KEYBOARD |
326                                              PP_INPUTEVENT_CLASS_TOUCH);
327   PostMessageBarrier();
328 
329   // Send the events and check that we received them.
330   ASSERT_TRUE(
331       SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN,
332                                           PP_INPUTEVENT_MOUSEBUTTON_LEFT)));
333   ASSERT_TRUE(
334       SimulateInputEvent(CreateWheelEvent()));
335   ASSERT_TRUE(
336       SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN,
337                                         kSpaceChar)));
338   ASSERT_TRUE(
339       SimulateInputEvent(CreateCharEvent(kSpaceString)));
340   ASSERT_TRUE(SimulateInputEvent(CreateTouchEvent(PP_INPUTEVENT_TYPE_TOUCHSTART,
341                                                   pp::FloatPoint(12, 23))));
342   // Request only mouse events.
343   input_event_interface_->ClearInputEventRequest(instance_->pp_instance(),
344                                                  PP_INPUTEVENT_CLASS_WHEEL |
345                                                  PP_INPUTEVENT_CLASS_KEYBOARD);
346   PostMessageBarrier();
347 
348   // Check that we only receive mouse events.
349   ASSERT_TRUE(
350       SimulateInputEvent(CreateMouseEvent(PP_INPUTEVENT_TYPE_MOUSEDOWN,
351                                           PP_INPUTEVENT_MOUSEBUTTON_LEFT)));
352   ASSERT_FALSE(
353       SimulateInputEvent(CreateWheelEvent()));
354   ASSERT_FALSE(
355       SimulateInputEvent(CreateKeyEvent(PP_INPUTEVENT_TYPE_KEYDOWN,
356                                         kSpaceChar)));
357   ASSERT_FALSE(
358       SimulateInputEvent(CreateCharEvent(kSpaceString)));
359 
360   PASS();
361 }
362 
TestAcceptTouchEvent_1()363 std::string TestInputEvent::TestAcceptTouchEvent_1() {
364   // The browser normally sends touch-events to the renderer only if the page
365   // has touch-event handlers. Since test-case.html does not have any
366   // touch-event handler, it would normally not receive any touch events from
367   // the browser. However, if a plugin in the page does accept touch events,
368   // then the browser should start sending touch-events to the page. In this
369   // test, the plugin simply registers for touch-events. The real test is to
370   // verify that the browser knows to send touch-events to the renderer.
371   // If the plugin is removed from the page, then there are no more touch-event
372   // handlers in the page, and browser stops sending touch-events. So to make
373   // it possible to test this properly, the plugin is not removed from the page
374   // at the end of the test.
375   instance_->set_remove_plugin(false);
376   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
377                                              PP_INPUTEVENT_CLASS_MOUSE |
378                                              PP_INPUTEVENT_CLASS_WHEEL |
379                                              PP_INPUTEVENT_CLASS_KEYBOARD |
380                                              PP_INPUTEVENT_CLASS_TOUCH);
381   PASS();
382 }
383 
TestAcceptTouchEvent_2()384 std::string TestInputEvent::TestAcceptTouchEvent_2() {
385   // See comment in TestAcceptTouchEvent_1.
386   instance_->set_remove_plugin(false);
387   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
388                                              PP_INPUTEVENT_CLASS_MOUSE |
389                                              PP_INPUTEVENT_CLASS_WHEEL |
390                                              PP_INPUTEVENT_CLASS_KEYBOARD |
391                                              PP_INPUTEVENT_CLASS_TOUCH);
392   input_event_interface_->ClearInputEventRequest(instance_->pp_instance(),
393                                                  PP_INPUTEVENT_CLASS_TOUCH);
394   PASS();
395 }
396 
TestAcceptTouchEvent_3()397 std::string TestInputEvent::TestAcceptTouchEvent_3() {
398   // See comment in TestAcceptTouchEvent_1.
399   instance_->set_remove_plugin(false);
400   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
401                                              PP_INPUTEVENT_CLASS_MOUSE |
402                                              PP_INPUTEVENT_CLASS_WHEEL |
403                                              PP_INPUTEVENT_CLASS_KEYBOARD);
404   input_event_interface_->RequestFilteringInputEvents(instance_->pp_instance(),
405       PP_INPUTEVENT_CLASS_TOUCH);
406   PASS();
407 }
408 
TestAcceptTouchEvent_4()409 std::string TestInputEvent::TestAcceptTouchEvent_4() {
410   // See comment in TestAcceptTouchEvent_1.
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   input_event_interface_->RequestInputEvents(instance_->pp_instance(),
417                                              PP_INPUTEVENT_CLASS_TOUCH);
418   PASS();
419 }
420 
421