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.
5 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
8 #include "content/browser/renderer_host/input/web_input_event_util.h"
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 #include "ui/gfx/geometry/safe_integer_conversions.h"
19 using blink::WebGestureEvent
;
20 using blink::WebInputEvent
;
21 using blink::WebTouchEvent
;
22 using blink::WebTouchPoint
;
23 using ui::MotionEvent
;
27 const char* GetKeyIdentifier(ui::KeyboardCode key_code
) {
31 case ui::VKEY_CONTROL
:
35 case ui::VKEY_CAPITAL
:
48 case ui::VKEY_EXECUTE
:
102 case ui::VKEY_INSERT
:
112 case ui::VKEY_SNAPSHOT
:
113 return "PrintScreen";
116 case ui::VKEY_SCROLL
:
118 case ui::VKEY_SELECT
:
122 case ui::VKEY_DELETE
:
123 return "U+007F"; // Standard says that DEL becomes U+007F.
124 case ui::VKEY_MEDIA_NEXT_TRACK
:
125 return "MediaNextTrack";
126 case ui::VKEY_MEDIA_PREV_TRACK
:
127 return "MediaPreviousTrack";
128 case ui::VKEY_MEDIA_STOP
:
130 case ui::VKEY_MEDIA_PLAY_PAUSE
:
131 return "MediaPlayPause";
132 case ui::VKEY_VOLUME_MUTE
:
134 case ui::VKEY_VOLUME_DOWN
:
136 case ui::VKEY_VOLUME_UP
:
143 WebInputEvent::Type
ToWebInputEventType(MotionEvent::Action action
) {
145 case MotionEvent::ACTION_DOWN
:
146 return WebInputEvent::TouchStart
;
147 case MotionEvent::ACTION_MOVE
:
148 return WebInputEvent::TouchMove
;
149 case MotionEvent::ACTION_UP
:
150 return WebInputEvent::TouchEnd
;
151 case MotionEvent::ACTION_CANCEL
:
152 return WebInputEvent::TouchCancel
;
153 case MotionEvent::ACTION_POINTER_DOWN
:
154 return WebInputEvent::TouchStart
;
155 case MotionEvent::ACTION_POINTER_UP
:
156 return WebInputEvent::TouchEnd
;
158 NOTREACHED() << "Invalid MotionEvent::Action.";
159 return WebInputEvent::Undefined
;
162 // Note that |is_action_pointer| is meaningful only in the context of
163 // |ACTION_POINTER_UP| and |ACTION_POINTER_DOWN|; other actions map directly to
164 // WebTouchPoint::State.
165 WebTouchPoint::State
ToWebTouchPointState(const MotionEvent
& event
,
166 size_t pointer_index
) {
167 switch (event
.GetAction()) {
168 case MotionEvent::ACTION_DOWN
:
169 return WebTouchPoint::StatePressed
;
170 case MotionEvent::ACTION_MOVE
:
171 return WebTouchPoint::StateMoved
;
172 case MotionEvent::ACTION_UP
:
173 return WebTouchPoint::StateReleased
;
174 case MotionEvent::ACTION_CANCEL
:
175 return WebTouchPoint::StateCancelled
;
176 case MotionEvent::ACTION_POINTER_DOWN
:
177 return static_cast<int>(pointer_index
) == event
.GetActionIndex()
178 ? WebTouchPoint::StatePressed
179 : WebTouchPoint::StateStationary
;
180 case MotionEvent::ACTION_POINTER_UP
:
181 return static_cast<int>(pointer_index
) == event
.GetActionIndex()
182 ? WebTouchPoint::StateReleased
183 : WebTouchPoint::StateStationary
;
185 NOTREACHED() << "Invalid MotionEvent::Action.";
186 return WebTouchPoint::StateUndefined
;
189 WebTouchPoint
CreateWebTouchPoint(const MotionEvent
& event
,
190 size_t pointer_index
) {
192 touch
.id
= event
.GetPointerId(pointer_index
);
193 touch
.state
= ToWebTouchPointState(event
, pointer_index
);
194 touch
.position
.x
= event
.GetX(pointer_index
);
195 touch
.position
.y
= event
.GetY(pointer_index
);
196 touch
.screenPosition
.x
= event
.GetRawX(pointer_index
);
197 touch
.screenPosition
.y
= event
.GetRawY(pointer_index
);
199 // A note on touch ellipse specifications:
201 // Android MotionEvent provides the major and minor axes of the touch ellipse,
202 // as well as the orientation of the major axis clockwise from vertical, in
204 // http://developer.android.com/reference/android/view/MotionEvent.html
206 // The proposed extension to W3C Touch Events specifies the touch ellipse
207 // using two radii along x- & y-axes and a positive acute rotation angle in
209 // http://dvcs.w3.org/hg/webevents/raw-file/default/touchevents.html
211 float major_radius
= event
.GetTouchMajor(pointer_index
) / 2.f
;
212 float minor_radius
= event
.GetTouchMinor(pointer_index
) / 2.f
;
214 DCHECK_LE(minor_radius
, major_radius
);
215 DCHECK_IMPLIES(major_radius
, minor_radius
);
217 float orientation_deg
= event
.GetOrientation(pointer_index
) * 180.f
/ M_PI
;
218 DCHECK_GE(major_radius
, 0);
219 DCHECK_GE(minor_radius
, 0);
220 DCHECK_GE(major_radius
, minor_radius
);
221 // Allow a small bound tolerance to account for floating point conversion.
222 DCHECK_GT(orientation_deg
, -90.01f
);
223 DCHECK_LT(orientation_deg
, 90.01f
);
224 if (orientation_deg
>= 0) {
225 // The case orientation_deg == 0 is handled here on purpose: although the
226 // 'else' block is equivalent in this case, we want to pass the 0 value
227 // unchanged (and 0 is the default value for many devices that don't
228 // report elliptical touches).
229 touch
.radiusX
= minor_radius
;
230 touch
.radiusY
= major_radius
;
231 touch
.rotationAngle
= orientation_deg
;
233 touch
.radiusX
= major_radius
;
234 touch
.radiusY
= minor_radius
;
235 touch
.rotationAngle
= orientation_deg
+ 90;
238 touch
.force
= event
.GetPressure(pointer_index
);
247 void UpdateWindowsKeyCodeAndKeyIdentifier(blink::WebKeyboardEvent
* event
,
248 ui::KeyboardCode windows_key_code
) {
249 event
->windowsKeyCode
= windows_key_code
;
251 const char* id
= GetKeyIdentifier(windows_key_code
);
253 base::strlcpy(event
->keyIdentifier
, id
, sizeof(event
->keyIdentifier
) - 1);
255 base::snprintf(event
->keyIdentifier
,
256 sizeof(event
->keyIdentifier
),
258 base::ToUpperASCII(static_cast<int>(windows_key_code
)));
262 blink::WebTouchEvent
CreateWebTouchEventFromMotionEvent(
263 const ui::MotionEvent
& event
,
264 bool may_cause_scrolling
) {
265 static_assert(static_cast<int>(MotionEvent::MAX_TOUCH_POINT_COUNT
) ==
266 static_cast<int>(blink::WebTouchEvent::touchesLengthCap
),
267 "inconsistent maximum number of active touch points");
269 blink::WebTouchEvent result
;
271 WebTouchEventTraits::ResetType(
272 ToWebInputEventType(event
.GetAction()),
273 (event
.GetEventTime() - base::TimeTicks()).InSecondsF(),
275 result
.causesScrollingIfUncanceled
= may_cause_scrolling
;
277 result
.modifiers
= EventFlagsToWebEventModifiers(event
.GetFlags());
278 result
.touchesLength
=
279 std::min(event
.GetPointerCount(),
280 static_cast<size_t>(WebTouchEvent::touchesLengthCap
));
281 DCHECK_GT(result
.touchesLength
, 0U);
283 for (size_t i
= 0; i
< result
.touchesLength
; ++i
)
284 result
.touches
[i
] = CreateWebTouchPoint(event
, i
);
289 WebGestureEvent
CreateWebGestureEvent(const ui::GestureEventDetails
& details
,
290 base::TimeDelta timestamp
,
291 const gfx::PointF
& location
,
292 const gfx::PointF
& raw_location
,
294 WebGestureEvent gesture
;
295 gesture
.timeStampSeconds
= timestamp
.InSecondsF();
296 gesture
.x
= gfx::ToFlooredInt(location
.x());
297 gesture
.y
= gfx::ToFlooredInt(location
.y());
298 gesture
.globalX
= gfx::ToFlooredInt(raw_location
.x());
299 gesture
.globalY
= gfx::ToFlooredInt(raw_location
.y());
300 gesture
.modifiers
= EventFlagsToWebEventModifiers(flags
);
301 gesture
.sourceDevice
= blink::WebGestureDeviceTouchscreen
;
303 switch (details
.type()) {
304 case ui::ET_GESTURE_SHOW_PRESS
:
305 gesture
.type
= WebInputEvent::GestureShowPress
;
306 gesture
.data
.showPress
.width
= details
.bounding_box_f().width();
307 gesture
.data
.showPress
.height
= details
.bounding_box_f().height();
309 case ui::ET_GESTURE_DOUBLE_TAP
:
310 gesture
.type
= WebInputEvent::GestureDoubleTap
;
311 DCHECK_EQ(1, details
.tap_count());
312 gesture
.data
.tap
.tapCount
= details
.tap_count();
313 gesture
.data
.tap
.width
= details
.bounding_box_f().width();
314 gesture
.data
.tap
.height
= details
.bounding_box_f().height();
316 case ui::ET_GESTURE_TAP
:
317 gesture
.type
= WebInputEvent::GestureTap
;
318 DCHECK_GE(details
.tap_count(), 1);
319 gesture
.data
.tap
.tapCount
= details
.tap_count();
320 gesture
.data
.tap
.width
= details
.bounding_box_f().width();
321 gesture
.data
.tap
.height
= details
.bounding_box_f().height();
323 case ui::ET_GESTURE_TAP_UNCONFIRMED
:
324 gesture
.type
= WebInputEvent::GestureTapUnconfirmed
;
325 DCHECK_EQ(1, details
.tap_count());
326 gesture
.data
.tap
.tapCount
= details
.tap_count();
327 gesture
.data
.tap
.width
= details
.bounding_box_f().width();
328 gesture
.data
.tap
.height
= details
.bounding_box_f().height();
330 case ui::ET_GESTURE_LONG_PRESS
:
331 gesture
.type
= WebInputEvent::GestureLongPress
;
332 gesture
.data
.longPress
.width
= details
.bounding_box_f().width();
333 gesture
.data
.longPress
.height
= details
.bounding_box_f().height();
335 case ui::ET_GESTURE_LONG_TAP
:
336 gesture
.type
= WebInputEvent::GestureLongTap
;
337 gesture
.data
.longPress
.width
= details
.bounding_box_f().width();
338 gesture
.data
.longPress
.height
= details
.bounding_box_f().height();
340 case ui::ET_GESTURE_TWO_FINGER_TAP
:
341 gesture
.type
= blink::WebInputEvent::GestureTwoFingerTap
;
342 gesture
.data
.twoFingerTap
.firstFingerWidth
= details
.first_finger_width();
343 gesture
.data
.twoFingerTap
.firstFingerHeight
=
344 details
.first_finger_height();
346 case ui::ET_GESTURE_SCROLL_BEGIN
:
347 gesture
.type
= WebInputEvent::GestureScrollBegin
;
348 gesture
.data
.scrollBegin
.deltaXHint
= details
.scroll_x_hint();
349 gesture
.data
.scrollBegin
.deltaYHint
= details
.scroll_y_hint();
351 case ui::ET_GESTURE_SCROLL_UPDATE
:
352 gesture
.type
= WebInputEvent::GestureScrollUpdate
;
353 gesture
.data
.scrollUpdate
.deltaX
= details
.scroll_x();
354 gesture
.data
.scrollUpdate
.deltaY
= details
.scroll_y();
355 gesture
.data
.scrollUpdate
.previousUpdateInSequencePrevented
=
356 details
.previous_scroll_update_in_sequence_prevented();
358 case ui::ET_GESTURE_SCROLL_END
:
359 gesture
.type
= WebInputEvent::GestureScrollEnd
;
361 case ui::ET_SCROLL_FLING_START
:
362 gesture
.type
= WebInputEvent::GestureFlingStart
;
363 gesture
.data
.flingStart
.velocityX
= details
.velocity_x();
364 gesture
.data
.flingStart
.velocityY
= details
.velocity_y();
366 case ui::ET_SCROLL_FLING_CANCEL
:
367 gesture
.type
= WebInputEvent::GestureFlingCancel
;
369 case ui::ET_GESTURE_PINCH_BEGIN
:
370 gesture
.type
= WebInputEvent::GesturePinchBegin
;
372 case ui::ET_GESTURE_PINCH_UPDATE
:
373 gesture
.type
= WebInputEvent::GesturePinchUpdate
;
374 gesture
.data
.pinchUpdate
.scale
= details
.scale();
376 case ui::ET_GESTURE_PINCH_END
:
377 gesture
.type
= WebInputEvent::GesturePinchEnd
;
379 case ui::ET_GESTURE_TAP_CANCEL
:
380 gesture
.type
= WebInputEvent::GestureTapCancel
;
382 case ui::ET_GESTURE_TAP_DOWN
:
383 gesture
.type
= WebInputEvent::GestureTapDown
;
384 gesture
.data
.tapDown
.width
= details
.bounding_box_f().width();
385 gesture
.data
.tapDown
.height
= details
.bounding_box_f().height();
387 case ui::ET_GESTURE_BEGIN
:
388 case ui::ET_GESTURE_END
:
389 case ui::ET_GESTURE_SWIPE
:
390 // The caller is responsible for discarding these gestures appropriately.
391 gesture
.type
= WebInputEvent::Undefined
;
394 NOTREACHED() << "ui::EventType provided wasn't a valid gesture event: "
401 WebGestureEvent
CreateWebGestureEventFromGestureEventData(
402 const ui::GestureEventData
& data
) {
403 return CreateWebGestureEvent(data
.details
,
404 data
.time
- base::TimeTicks(),
405 gfx::PointF(data
.x
, data
.y
),
406 gfx::PointF(data
.raw_x
, data
.raw_y
),
410 int EventFlagsToWebEventModifiers(int flags
) {
413 if (flags
& ui::EF_SHIFT_DOWN
)
414 modifiers
|= blink::WebInputEvent::ShiftKey
;
415 if (flags
& ui::EF_CONTROL_DOWN
)
416 modifiers
|= blink::WebInputEvent::ControlKey
;
417 if (flags
& ui::EF_ALT_DOWN
)
418 modifiers
|= blink::WebInputEvent::AltKey
;
419 if (flags
& ui::EF_COMMAND_DOWN
)
420 modifiers
|= blink::WebInputEvent::MetaKey
;
422 if (flags
& ui::EF_LEFT_MOUSE_BUTTON
)
423 modifiers
|= blink::WebInputEvent::LeftButtonDown
;
424 if (flags
& ui::EF_MIDDLE_MOUSE_BUTTON
)
425 modifiers
|= blink::WebInputEvent::MiddleButtonDown
;
426 if (flags
& ui::EF_RIGHT_MOUSE_BUTTON
)
427 modifiers
|= blink::WebInputEvent::RightButtonDown
;
428 if (flags
& ui::EF_CAPS_LOCK_DOWN
)
429 modifiers
|= blink::WebInputEvent::CapsLockOn
;
430 if (flags
& ui::EF_IS_REPEAT
)
431 modifiers
|= blink::WebInputEvent::IsAutoRepeat
;
432 if (flags
& ui::EF_NUMPAD_KEY
)
433 modifiers
|= blink::WebInputEvent::IsKeyPad
;
438 int WebEventModifiersToEventFlags(int modifiers
) {
441 if (modifiers
& blink::WebInputEvent::ShiftKey
)
442 flags
|= ui::EF_SHIFT_DOWN
;
443 if (modifiers
& blink::WebInputEvent::ControlKey
)
444 flags
|= ui::EF_CONTROL_DOWN
;
445 if (modifiers
& blink::WebInputEvent::AltKey
)
446 flags
|= ui::EF_ALT_DOWN
;
447 if (modifiers
& blink::WebInputEvent::MetaKey
)
448 flags
|= ui::EF_COMMAND_DOWN
;
450 if (modifiers
& blink::WebInputEvent::LeftButtonDown
)
451 flags
|= ui::EF_LEFT_MOUSE_BUTTON
;
452 if (modifiers
& blink::WebInputEvent::MiddleButtonDown
)
453 flags
|= ui::EF_MIDDLE_MOUSE_BUTTON
;
454 if (modifiers
& blink::WebInputEvent::RightButtonDown
)
455 flags
|= ui::EF_RIGHT_MOUSE_BUTTON
;
456 if (modifiers
& blink::WebInputEvent::CapsLockOn
)
457 flags
|= ui::EF_CAPS_LOCK_DOWN
;
458 if (modifiers
& blink::WebInputEvent::IsAutoRepeat
)
459 flags
|= ui::EF_IS_REPEAT
;
460 if (modifiers
& blink::WebInputEvent::IsKeyPad
)
461 flags
|= ui::EF_NUMPAD_KEY
;
466 } // namespace content