• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 using blink::WebGestureEvent;
19 using blink::WebInputEvent;
20 using blink::WebTouchEvent;
21 using blink::WebTouchPoint;
22 using ui::MotionEvent;
23 
24 namespace {
25 
GetKeyIdentifier(ui::KeyboardCode key_code)26 const 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 
ToWebInputEventType(MotionEvent::Action action)142 WebInputEvent::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.
ToWebTouchPointState(MotionEvent::Action action,bool is_action_pointer)164 WebTouchPoint::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 
CreateWebTouchPoint(const MotionEvent & event,size_t pointer_index)186 WebTouchPoint 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 
239 namespace content {
240 
UpdateWindowsKeyCodeAndKeyIdentifier(blink::WebKeyboardEvent * event,ui::KeyboardCode windows_key_code)241 void 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 
CreateWebTouchEventFromMotionEvent(const ui::MotionEvent & event)256 blink::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 
CreateWebGestureEventFromGestureEventData(const ui::GestureEventData & data)281 WebGestureEvent 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 
EventFlagsToWebEventModifiers(int flags)381 int 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 
WebEventModifiersToEventFlags(int modifiers)409 int 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