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