1// Copyright 2013 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// MSVC++ requires this to be set before any other includes to get M_PI.
6#define _USE_MATH_DEFINES
7
8#include "content/browser/renderer_host/input/web_input_event_util.h"
9
10#include <cmath>
11
12#include "base/strings/string_util.h"
13#include "content/common/input/web_touch_event_traits.h"
14#include "ui/events/event_constants.h"
15#include "ui/events/gesture_detection/gesture_event_data.h"
16#include "ui/events/gesture_detection/motion_event.h"
17
18using blink::WebGestureEvent;
19using blink::WebInputEvent;
20using blink::WebTouchEvent;
21using blink::WebTouchPoint;
22using ui::MotionEvent;
23
24namespace {
25
26const char* GetKeyIdentifier(ui::KeyboardCode key_code) {
27  switch (key_code) {
28    case ui::VKEY_MENU:
29      return "Alt";
30    case ui::VKEY_CONTROL:
31      return "Control";
32    case ui::VKEY_SHIFT:
33      return "Shift";
34    case ui::VKEY_CAPITAL:
35      return "CapsLock";
36    case ui::VKEY_LWIN:
37    case ui::VKEY_RWIN:
38      return "Win";
39    case ui::VKEY_CLEAR:
40      return "Clear";
41    case ui::VKEY_DOWN:
42      return "Down";
43    case ui::VKEY_END:
44      return "End";
45    case ui::VKEY_RETURN:
46      return "Enter";
47    case ui::VKEY_EXECUTE:
48      return "Execute";
49    case ui::VKEY_F1:
50      return "F1";
51    case ui::VKEY_F2:
52      return "F2";
53    case ui::VKEY_F3:
54      return "F3";
55    case ui::VKEY_F4:
56      return "F4";
57    case ui::VKEY_F5:
58      return "F5";
59    case ui::VKEY_F6:
60      return "F6";
61    case ui::VKEY_F7:
62      return "F7";
63    case ui::VKEY_F8:
64      return "F8";
65    case ui::VKEY_F9:
66      return "F9";
67    case ui::VKEY_F10:
68      return "F10";
69    case ui::VKEY_F11:
70      return "F11";
71    case ui::VKEY_F12:
72      return "F12";
73    case ui::VKEY_F13:
74      return "F13";
75    case ui::VKEY_F14:
76      return "F14";
77    case ui::VKEY_F15:
78      return "F15";
79    case ui::VKEY_F16:
80      return "F16";
81    case ui::VKEY_F17:
82      return "F17";
83    case ui::VKEY_F18:
84      return "F18";
85    case ui::VKEY_F19:
86      return "F19";
87    case ui::VKEY_F20:
88      return "F20";
89    case ui::VKEY_F21:
90      return "F21";
91    case ui::VKEY_F22:
92      return "F22";
93    case ui::VKEY_F23:
94      return "F23";
95    case ui::VKEY_F24:
96      return "F24";
97    case ui::VKEY_HELP:
98      return "Help";
99    case ui::VKEY_HOME:
100      return "Home";
101    case ui::VKEY_INSERT:
102      return "Insert";
103    case ui::VKEY_LEFT:
104      return "Left";
105    case ui::VKEY_NEXT:
106      return "PageDown";
107    case ui::VKEY_PRIOR:
108      return "PageUp";
109    case ui::VKEY_PAUSE:
110      return "Pause";
111    case ui::VKEY_SNAPSHOT:
112      return "PrintScreen";
113    case ui::VKEY_RIGHT:
114      return "Right";
115    case ui::VKEY_SCROLL:
116      return "Scroll";
117    case ui::VKEY_SELECT:
118      return "Select";
119    case ui::VKEY_UP:
120      return "Up";
121    case ui::VKEY_DELETE:
122      return "U+007F";  // Standard says that DEL becomes U+007F.
123    case ui::VKEY_MEDIA_NEXT_TRACK:
124      return "MediaNextTrack";
125    case ui::VKEY_MEDIA_PREV_TRACK:
126      return "MediaPreviousTrack";
127    case ui::VKEY_MEDIA_STOP:
128      return "MediaStop";
129    case ui::VKEY_MEDIA_PLAY_PAUSE:
130      return "MediaPlayPause";
131    case ui::VKEY_VOLUME_MUTE:
132      return "VolumeMute";
133    case ui::VKEY_VOLUME_DOWN:
134      return "VolumeDown";
135    case ui::VKEY_VOLUME_UP:
136      return "VolumeUp";
137    default:
138      return NULL;
139  };
140}
141
142WebInputEvent::Type ToWebInputEventType(MotionEvent::Action action) {
143  switch (action) {
144    case MotionEvent::ACTION_DOWN:
145      return WebInputEvent::TouchStart;
146    case MotionEvent::ACTION_MOVE:
147      return WebInputEvent::TouchMove;
148    case MotionEvent::ACTION_UP:
149      return WebInputEvent::TouchEnd;
150    case MotionEvent::ACTION_CANCEL:
151      return WebInputEvent::TouchCancel;
152    case MotionEvent::ACTION_POINTER_DOWN:
153      return WebInputEvent::TouchStart;
154    case MotionEvent::ACTION_POINTER_UP:
155      return WebInputEvent::TouchEnd;
156  }
157  NOTREACHED() << "Invalid MotionEvent::Action.";
158  return WebInputEvent::Undefined;
159}
160
161// Note that |is_action_pointer| is meaningful only in the context of
162// |ACTION_POINTER_UP| and |ACTION_POINTER_DOWN|; other actions map directly to
163// WebTouchPoint::State.
164WebTouchPoint::State ToWebTouchPointState(MotionEvent::Action action,
165                                          bool is_action_pointer) {
166  switch (action) {
167    case MotionEvent::ACTION_DOWN:
168      return WebTouchPoint::StatePressed;
169    case MotionEvent::ACTION_MOVE:
170      return WebTouchPoint::StateMoved;
171    case MotionEvent::ACTION_UP:
172      return WebTouchPoint::StateReleased;
173    case MotionEvent::ACTION_CANCEL:
174      return WebTouchPoint::StateCancelled;
175    case MotionEvent::ACTION_POINTER_DOWN:
176      return is_action_pointer ? WebTouchPoint::StatePressed
177                               : WebTouchPoint::StateStationary;
178    case MotionEvent::ACTION_POINTER_UP:
179      return is_action_pointer ? WebTouchPoint::StateReleased
180                               : WebTouchPoint::StateStationary;
181  }
182  NOTREACHED() << "Invalid MotionEvent::Action.";
183  return WebTouchPoint::StateUndefined;
184}
185
186WebTouchPoint CreateWebTouchPoint(const MotionEvent& event,
187                                  size_t pointer_index) {
188  WebTouchPoint touch;
189  touch.id = event.GetPointerId(pointer_index);
190  touch.state = ToWebTouchPointState(
191      event.GetAction(),
192      static_cast<int>(pointer_index) == event.GetActionIndex());
193  touch.position.x = event.GetX(pointer_index);
194  touch.position.y = event.GetY(pointer_index);
195  touch.screenPosition.x = event.GetRawX(pointer_index);
196  touch.screenPosition.y = event.GetRawY(pointer_index);
197
198  // A note on touch ellipse specifications:
199  //
200  // Android MotionEvent provides the major and minor axes of the touch ellipse,
201  // as well as the orientation of the major axis clockwise from vertical, in
202  // radians. See:
203  // http://developer.android.com/reference/android/view/MotionEvent.html
204  //
205  // The proposed extension to W3C Touch Events specifies the touch ellipse
206  // using two radii along x- & y-axes and a positive acute rotation angle in
207  // degrees. See:
208  // http://dvcs.w3.org/hg/webevents/raw-file/default/touchevents.html
209
210  float major_radius = event.GetTouchMajor(pointer_index) / 2.f;
211  float minor_radius = event.GetTouchMinor(pointer_index) / 2.f;
212  float orientation_deg = event.GetOrientation(pointer_index) * 180.f / M_PI;
213  DCHECK_GE(major_radius, 0) << "Unexpected touch major < 0";
214  DCHECK_GE(minor_radius, 0) << "Unexpected touch minor < 0";
215  DCHECK_GE(major_radius, minor_radius) << "Unexpected major/minor touch radii";
216  DCHECK(-90 <= orientation_deg && orientation_deg <= 90)
217      << "Unexpected touch orientation angle";
218  if (orientation_deg >= 0) {
219    // The case orientation_deg == 0 is handled here on purpose: although the
220    // 'else' block is equivalent in this case, we want to pass the 0 value
221    // unchanged (and 0 is the default value for many devices that don't
222    // report elliptical touches).
223    touch.radiusX = minor_radius;
224    touch.radiusY = major_radius;
225    touch.rotationAngle = orientation_deg;
226  } else {
227    touch.radiusX = major_radius;
228    touch.radiusY = minor_radius;
229    touch.rotationAngle = orientation_deg + 90;
230  }
231
232  touch.force = event.GetPressure(pointer_index);
233
234  return touch;
235}
236
237}  // namespace
238
239namespace content {
240
241void UpdateWindowsKeyCodeAndKeyIdentifier(blink::WebKeyboardEvent* event,
242                                          ui::KeyboardCode windows_key_code) {
243  event->windowsKeyCode = windows_key_code;
244
245  const char* id = GetKeyIdentifier(windows_key_code);
246  if (id) {
247    base::strlcpy(event->keyIdentifier, id, sizeof(event->keyIdentifier) - 1);
248  } else {
249    base::snprintf(event->keyIdentifier,
250                   sizeof(event->keyIdentifier),
251                   "U+%04X",
252                   base::ToUpperASCII(static_cast<int>(windows_key_code)));
253  }
254}
255
256blink::WebTouchEvent CreateWebTouchEventFromMotionEvent(
257    const ui::MotionEvent& event) {
258  COMPILE_ASSERT(static_cast<int>(MotionEvent::MAX_TOUCH_POINT_COUNT) ==
259                     static_cast<int>(blink::WebTouchEvent::touchesLengthCap),
260                 inconsistent_maximum_number_of_active_touch_points);
261
262  blink::WebTouchEvent result;
263
264  WebTouchEventTraits::ResetType(
265      ToWebInputEventType(event.GetAction()),
266      (event.GetEventTime() - base::TimeTicks()).InSecondsF(),
267      &result);
268
269  result.modifiers = EventFlagsToWebEventModifiers(event.GetFlags());
270  result.touchesLength =
271      std::min(event.GetPointerCount(),
272               static_cast<size_t>(WebTouchEvent::touchesLengthCap));
273  DCHECK_GT(result.touchesLength, 0U);
274
275  for (size_t i = 0; i < result.touchesLength; ++i)
276    result.touches[i] = CreateWebTouchPoint(event, i);
277
278  return result;
279}
280
281WebGestureEvent CreateWebGestureEventFromGestureEventData(
282    const ui::GestureEventData& data) {
283  WebGestureEvent gesture;
284  gesture.modifiers = EventFlagsToWebEventModifiers(data.flags);
285  gesture.x = data.x;
286  gesture.y = data.y;
287  gesture.globalX = data.raw_x;
288  gesture.globalY = data.raw_y;
289  gesture.timeStampSeconds = (data.time - base::TimeTicks()).InSecondsF();
290  gesture.sourceDevice = blink::WebGestureDeviceTouchscreen;
291
292  switch (data.type()) {
293    case ui::ET_GESTURE_SHOW_PRESS:
294      gesture.type = WebInputEvent::GestureShowPress;
295      gesture.data.showPress.width = data.details.bounding_box_f().width();
296      gesture.data.showPress.height = data.details.bounding_box_f().height();
297      break;
298    case ui::ET_GESTURE_DOUBLE_TAP:
299      gesture.type = WebInputEvent::GestureDoubleTap;
300      DCHECK_EQ(1, data.details.tap_count());
301      gesture.data.tap.tapCount = data.details.tap_count();
302      gesture.data.tap.width = data.details.bounding_box_f().width();
303      gesture.data.tap.height = data.details.bounding_box_f().height();
304      break;
305    case ui::ET_GESTURE_TAP:
306      gesture.type = WebInputEvent::GestureTap;
307      DCHECK_EQ(1, data.details.tap_count());
308      gesture.data.tap.tapCount = data.details.tap_count();
309      gesture.data.tap.width = data.details.bounding_box_f().width();
310      gesture.data.tap.height = data.details.bounding_box_f().height();
311      break;
312    case ui::ET_GESTURE_TAP_UNCONFIRMED:
313      gesture.type = WebInputEvent::GestureTapUnconfirmed;
314      DCHECK_EQ(1, data.details.tap_count());
315      gesture.data.tap.tapCount = data.details.tap_count();
316      gesture.data.tap.width = data.details.bounding_box_f().width();
317      gesture.data.tap.height = data.details.bounding_box_f().height();
318      break;
319    case ui::ET_GESTURE_LONG_PRESS:
320      gesture.type = WebInputEvent::GestureLongPress;
321      gesture.data.longPress.width = data.details.bounding_box_f().width();
322      gesture.data.longPress.height = data.details.bounding_box_f().height();
323      break;
324    case ui::ET_GESTURE_LONG_TAP:
325      gesture.type = WebInputEvent::GestureLongTap;
326      gesture.data.longPress.width = data.details.bounding_box_f().width();
327      gesture.data.longPress.height = data.details.bounding_box_f().height();
328      break;
329    case ui::ET_GESTURE_SCROLL_BEGIN:
330      gesture.type = WebInputEvent::GestureScrollBegin;
331      gesture.data.scrollBegin.deltaXHint = data.details.scroll_x_hint();
332      gesture.data.scrollBegin.deltaYHint = data.details.scroll_y_hint();
333      break;
334    case ui::ET_GESTURE_SCROLL_UPDATE:
335      gesture.type = WebInputEvent::GestureScrollUpdate;
336      gesture.data.scrollUpdate.deltaX = data.details.scroll_x();
337      gesture.data.scrollUpdate.deltaY = data.details.scroll_y();
338      break;
339    case ui::ET_GESTURE_SCROLL_END:
340      gesture.type = WebInputEvent::GestureScrollEnd;
341      break;
342    case ui::ET_SCROLL_FLING_START:
343      gesture.type = WebInputEvent::GestureFlingStart;
344      gesture.data.flingStart.velocityX = data.details.velocity_x();
345      gesture.data.flingStart.velocityY = data.details.velocity_y();
346      break;
347    case ui::ET_SCROLL_FLING_CANCEL:
348      gesture.type = WebInputEvent::GestureFlingCancel;
349      break;
350    case ui::ET_GESTURE_PINCH_BEGIN:
351      gesture.type = WebInputEvent::GesturePinchBegin;
352      break;
353    case ui::ET_GESTURE_PINCH_UPDATE:
354      gesture.type = WebInputEvent::GesturePinchUpdate;
355      gesture.data.pinchUpdate.scale = data.details.scale();
356      break;
357    case ui::ET_GESTURE_PINCH_END:
358      gesture.type = WebInputEvent::GesturePinchEnd;
359      break;
360    case ui::ET_GESTURE_TAP_CANCEL:
361      gesture.type = WebInputEvent::GestureTapCancel;
362      break;
363    case ui::ET_GESTURE_TAP_DOWN:
364      gesture.type = WebInputEvent::GestureTapDown;
365      gesture.data.tapDown.width = data.details.bounding_box_f().width();
366      gesture.data.tapDown.height = data.details.bounding_box_f().height();
367      break;
368    case ui::ET_GESTURE_BEGIN:
369    case ui::ET_GESTURE_END:
370      NOTREACHED() << "ET_GESTURE_BEGIN and ET_GESTURE_END are only produced "
371                   << "in Aura, and should never end up here.";
372      break;
373    default:
374      NOTREACHED() << "ui::EventType provided wasn't a valid gesture event.";
375      break;
376  }
377
378  return gesture;
379}
380
381int EventFlagsToWebEventModifiers(int flags) {
382  int modifiers = 0;
383
384  if (flags & ui::EF_SHIFT_DOWN)
385    modifiers |= blink::WebInputEvent::ShiftKey;
386  if (flags & ui::EF_CONTROL_DOWN)
387    modifiers |= blink::WebInputEvent::ControlKey;
388  if (flags & ui::EF_ALT_DOWN)
389    modifiers |= blink::WebInputEvent::AltKey;
390  if (flags & ui::EF_COMMAND_DOWN)
391    modifiers |= blink::WebInputEvent::MetaKey;
392
393  if (flags & ui::EF_LEFT_MOUSE_BUTTON)
394    modifiers |= blink::WebInputEvent::LeftButtonDown;
395  if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
396    modifiers |= blink::WebInputEvent::MiddleButtonDown;
397  if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
398    modifiers |= blink::WebInputEvent::RightButtonDown;
399  if (flags & ui::EF_CAPS_LOCK_DOWN)
400    modifiers |= blink::WebInputEvent::CapsLockOn;
401  if (flags & ui::EF_IS_REPEAT)
402    modifiers |= blink::WebInputEvent::IsAutoRepeat;
403  if (flags & ui::EF_NUMPAD_KEY)
404    modifiers |= blink::WebInputEvent::IsKeyPad;
405
406  return modifiers;
407}
408
409int WebEventModifiersToEventFlags(int modifiers) {
410  int flags = 0;
411
412  if (modifiers & blink::WebInputEvent::ShiftKey)
413    flags |= ui::EF_SHIFT_DOWN;
414  if (modifiers & blink::WebInputEvent::ControlKey)
415    flags |= ui::EF_CONTROL_DOWN;
416  if (modifiers & blink::WebInputEvent::AltKey)
417    flags |= ui::EF_ALT_DOWN;
418  if (modifiers & blink::WebInputEvent::MetaKey)
419    flags |= ui::EF_COMMAND_DOWN;
420
421  if (modifiers & blink::WebInputEvent::LeftButtonDown)
422    flags |= ui::EF_LEFT_MOUSE_BUTTON;
423  if (modifiers & blink::WebInputEvent::MiddleButtonDown)
424    flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
425  if (modifiers & blink::WebInputEvent::RightButtonDown)
426    flags |= ui::EF_RIGHT_MOUSE_BUTTON;
427  if (modifiers & blink::WebInputEvent::CapsLockOn)
428    flags |= ui::EF_CAPS_LOCK_DOWN;
429  if (modifiers & blink::WebInputEvent::IsAutoRepeat)
430    flags |= ui::EF_IS_REPEAT;
431  if (modifiers & blink::WebInputEvent::IsKeyPad)
432    flags |= ui::EF_NUMPAD_KEY;
433
434  return flags;
435}
436
437}  // namespace content
438