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 "content/browser/renderer_host/ui_events_helper.h"
6
7 #include "content/common/input/web_touch_event_traits.h"
8 #include "third_party/WebKit/public/web/WebInputEvent.h"
9 #include "ui/events/event.h"
10 #include "ui/events/event_constants.h"
11
12 namespace {
13
WebModifiersToUIFlags(int modifiers)14 int WebModifiersToUIFlags(int modifiers) {
15 int flags = ui::EF_NONE;
16
17 if (modifiers & blink::WebInputEvent::ShiftKey)
18 flags |= ui::EF_SHIFT_DOWN;
19 if (modifiers & blink::WebInputEvent::ControlKey)
20 flags |= ui::EF_CONTROL_DOWN;
21 if (modifiers & blink::WebInputEvent::AltKey)
22 flags |= ui::EF_ALT_DOWN;
23
24 if (modifiers & blink::WebInputEvent::LeftButtonDown)
25 flags |= ui::EF_LEFT_MOUSE_BUTTON;
26 if (modifiers & blink::WebInputEvent::RightButtonDown)
27 flags |= ui::EF_RIGHT_MOUSE_BUTTON;
28 if (modifiers & blink::WebInputEvent::MiddleButtonDown)
29 flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
30
31 if (modifiers & blink::WebInputEvent::CapsLockOn)
32 flags |= ui::EF_CAPS_LOCK_DOWN;
33
34 return flags;
35 }
36
WebTouchPointStateToEventType(blink::WebTouchPoint::State state)37 ui::EventType WebTouchPointStateToEventType(
38 blink::WebTouchPoint::State state) {
39 switch (state) {
40 case blink::WebTouchPoint::StateReleased:
41 return ui::ET_TOUCH_RELEASED;
42
43 case blink::WebTouchPoint::StatePressed:
44 return ui::ET_TOUCH_PRESSED;
45
46 case blink::WebTouchPoint::StateMoved:
47 return ui::ET_TOUCH_MOVED;
48
49 case blink::WebTouchPoint::StateCancelled:
50 return ui::ET_TOUCH_CANCELLED;
51
52 default:
53 return ui::ET_UNKNOWN;
54 }
55 }
56
TouchPointStateFromEvent(const ui::TouchEvent & event)57 blink::WebTouchPoint::State TouchPointStateFromEvent(
58 const ui::TouchEvent& event) {
59 switch (event.type()) {
60 case ui::ET_TOUCH_PRESSED:
61 return blink::WebTouchPoint::StatePressed;
62 case ui::ET_TOUCH_RELEASED:
63 return blink::WebTouchPoint::StateReleased;
64 case ui::ET_TOUCH_MOVED:
65 return blink::WebTouchPoint::StateMoved;
66 case ui::ET_TOUCH_CANCELLED:
67 return blink::WebTouchPoint::StateCancelled;
68 default:
69 return blink::WebTouchPoint::StateUndefined;
70 }
71 }
72
TouchEventTypeFromEvent(const ui::TouchEvent & event)73 blink::WebInputEvent::Type TouchEventTypeFromEvent(
74 const ui::TouchEvent& event) {
75 switch (event.type()) {
76 case ui::ET_TOUCH_PRESSED:
77 return blink::WebInputEvent::TouchStart;
78 case ui::ET_TOUCH_RELEASED:
79 return blink::WebInputEvent::TouchEnd;
80 case ui::ET_TOUCH_MOVED:
81 return blink::WebInputEvent::TouchMove;
82 case ui::ET_TOUCH_CANCELLED:
83 return blink::WebInputEvent::TouchCancel;
84 default:
85 return blink::WebInputEvent::Undefined;
86 }
87 }
88
89 } // namespace
90
91 namespace content {
92
MakeUITouchEventsFromWebTouchEvents(const TouchEventWithLatencyInfo & touch_with_latency,ScopedVector<ui::TouchEvent> * list,TouchEventCoordinateSystem coordinate_system)93 bool MakeUITouchEventsFromWebTouchEvents(
94 const TouchEventWithLatencyInfo& touch_with_latency,
95 ScopedVector<ui::TouchEvent>* list,
96 TouchEventCoordinateSystem coordinate_system) {
97 const blink::WebTouchEvent& touch = touch_with_latency.event;
98 ui::EventType type = ui::ET_UNKNOWN;
99 switch (touch.type) {
100 case blink::WebInputEvent::TouchStart:
101 type = ui::ET_TOUCH_PRESSED;
102 break;
103 case blink::WebInputEvent::TouchEnd:
104 type = ui::ET_TOUCH_RELEASED;
105 break;
106 case blink::WebInputEvent::TouchMove:
107 type = ui::ET_TOUCH_MOVED;
108 break;
109 case blink::WebInputEvent::TouchCancel:
110 type = ui::ET_TOUCH_CANCELLED;
111 break;
112 default:
113 NOTREACHED();
114 return false;
115 }
116
117 int flags = WebModifiersToUIFlags(touch.modifiers);
118 base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
119 static_cast<int64>(touch.timeStampSeconds * 1000000));
120 for (unsigned i = 0; i < touch.touchesLength; ++i) {
121 const blink::WebTouchPoint& point = touch.touches[i];
122 if (WebTouchPointStateToEventType(point.state) != type)
123 continue;
124 // ui events start in the co-ordinate space of the EventDispatcher.
125 gfx::PointF location;
126 if (coordinate_system == LOCAL_COORDINATES)
127 location = point.position;
128 else
129 location = point.screenPosition;
130 ui::TouchEvent* uievent = new ui::TouchEvent(type,
131 location,
132 flags,
133 point.id,
134 timestamp,
135 point.radiusX,
136 point.radiusY,
137 point.rotationAngle,
138 point.force);
139 uievent->set_latency(touch_with_latency.latency);
140 list->push_back(uievent);
141 }
142 return true;
143 }
144
MakeWebGestureEventFromUIEvent(const ui::GestureEvent & event)145 blink::WebGestureEvent MakeWebGestureEventFromUIEvent(
146 const ui::GestureEvent& event) {
147 blink::WebGestureEvent gesture_event;
148
149 switch (event.type()) {
150 case ui::ET_GESTURE_TAP:
151 gesture_event.type = blink::WebInputEvent::GestureTap;
152 gesture_event.data.tap.tapCount = event.details().tap_count();
153 gesture_event.data.tap.width = event.details().bounding_box().width();
154 gesture_event.data.tap.height = event.details().bounding_box().height();
155 break;
156 case ui::ET_GESTURE_TAP_DOWN:
157 gesture_event.type = blink::WebInputEvent::GestureTapDown;
158 gesture_event.data.tapDown.width =
159 event.details().bounding_box().width();
160 gesture_event.data.tapDown.height =
161 event.details().bounding_box().height();
162 break;
163 case ui::ET_GESTURE_SHOW_PRESS:
164 gesture_event.type = blink::WebInputEvent::GestureShowPress;
165 gesture_event.data.showPress.width =
166 event.details().bounding_box().width();
167 gesture_event.data.showPress.height =
168 event.details().bounding_box().height();
169 break;
170 case ui::ET_GESTURE_TAP_CANCEL:
171 gesture_event.type = blink::WebInputEvent::GestureTapCancel;
172 break;
173 case ui::ET_GESTURE_SCROLL_BEGIN:
174 gesture_event.type = blink::WebInputEvent::GestureScrollBegin;
175 gesture_event.data.scrollBegin.deltaXHint =
176 event.details().scroll_x_hint();
177 gesture_event.data.scrollBegin.deltaYHint =
178 event.details().scroll_y_hint();
179 break;
180 case ui::ET_GESTURE_SCROLL_UPDATE:
181 gesture_event.type = blink::WebInputEvent::GestureScrollUpdate;
182 gesture_event.data.scrollUpdate.deltaX = event.details().scroll_x();
183 gesture_event.data.scrollUpdate.deltaY = event.details().scroll_y();
184 break;
185 case ui::ET_GESTURE_SCROLL_END:
186 gesture_event.type = blink::WebInputEvent::GestureScrollEnd;
187 break;
188 case ui::ET_GESTURE_PINCH_BEGIN:
189 gesture_event.type = blink::WebInputEvent::GesturePinchBegin;
190 break;
191 case ui::ET_GESTURE_PINCH_UPDATE:
192 gesture_event.type = blink::WebInputEvent::GesturePinchUpdate;
193 gesture_event.data.pinchUpdate.scale = event.details().scale();
194 break;
195 case ui::ET_GESTURE_PINCH_END:
196 gesture_event.type = blink::WebInputEvent::GesturePinchEnd;
197 break;
198 case ui::ET_SCROLL_FLING_START:
199 gesture_event.type = blink::WebInputEvent::GestureFlingStart;
200 gesture_event.data.flingStart.velocityX = event.details().velocity_x();
201 gesture_event.data.flingStart.velocityY = event.details().velocity_y();
202 break;
203 case ui::ET_SCROLL_FLING_CANCEL:
204 gesture_event.type = blink::WebInputEvent::GestureFlingCancel;
205 break;
206 case ui::ET_GESTURE_LONG_PRESS:
207 gesture_event.type = blink::WebInputEvent::GestureLongPress;
208 gesture_event.data.longPress.width =
209 event.details().bounding_box().width();
210 gesture_event.data.longPress.height =
211 event.details().bounding_box().height();
212 break;
213 case ui::ET_GESTURE_LONG_TAP:
214 gesture_event.type = blink::WebInputEvent::GestureLongTap;
215 gesture_event.data.longPress.width =
216 event.details().bounding_box().width();
217 gesture_event.data.longPress.height =
218 event.details().bounding_box().height();
219 break;
220 case ui::ET_GESTURE_TWO_FINGER_TAP:
221 gesture_event.type = blink::WebInputEvent::GestureTwoFingerTap;
222 gesture_event.data.twoFingerTap.firstFingerWidth =
223 event.details().first_finger_width();
224 gesture_event.data.twoFingerTap.firstFingerHeight =
225 event.details().first_finger_height();
226 break;
227 case ui::ET_GESTURE_BEGIN:
228 case ui::ET_GESTURE_END:
229 case ui::ET_GESTURE_SWIPE:
230 gesture_event.type = blink::WebInputEvent::Undefined;
231 break;
232 default:
233 NOTREACHED() << "Unknown gesture type: " << event.type();
234 }
235
236 gesture_event.sourceDevice = blink::WebGestureDeviceTouchscreen;
237 gesture_event.modifiers = EventFlagsToWebEventModifiers(event.flags());
238 gesture_event.timeStampSeconds = event.time_stamp().InSecondsF();
239
240 return gesture_event;
241 }
242
EventFlagsToWebEventModifiers(int flags)243 int EventFlagsToWebEventModifiers(int flags) {
244 int modifiers = 0;
245
246 if (flags & ui::EF_SHIFT_DOWN)
247 modifiers |= blink::WebInputEvent::ShiftKey;
248 if (flags & ui::EF_CONTROL_DOWN)
249 modifiers |= blink::WebInputEvent::ControlKey;
250 if (flags & ui::EF_ALT_DOWN)
251 modifiers |= blink::WebInputEvent::AltKey;
252 // TODO(beng): MetaKey/META_MASK
253 if (flags & ui::EF_LEFT_MOUSE_BUTTON)
254 modifiers |= blink::WebInputEvent::LeftButtonDown;
255 if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
256 modifiers |= blink::WebInputEvent::MiddleButtonDown;
257 if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
258 modifiers |= blink::WebInputEvent::RightButtonDown;
259 if (flags & ui::EF_CAPS_LOCK_DOWN)
260 modifiers |= blink::WebInputEvent::CapsLockOn;
261 return modifiers;
262 }
263
UpdateWebTouchEventFromUIEvent(const ui::TouchEvent & event,blink::WebTouchEvent * web_event)264 blink::WebTouchPoint* UpdateWebTouchEventFromUIEvent(
265 const ui::TouchEvent& event,
266 blink::WebTouchEvent* web_event) {
267 blink::WebTouchPoint* point = NULL;
268 switch (event.type()) {
269 case ui::ET_TOUCH_PRESSED:
270 // Add a new touch point.
271 if (web_event->touchesLength < blink::WebTouchEvent::touchesLengthCap) {
272 point = &web_event->touches[web_event->touchesLength++];
273 point->id = event.touch_id();
274 }
275 break;
276 case ui::ET_TOUCH_RELEASED:
277 case ui::ET_TOUCH_CANCELLED:
278 case ui::ET_TOUCH_MOVED: {
279 // The touch point should have been added to the event from an earlier
280 // _PRESSED event. So find that.
281 // At the moment, only a maximum of 4 touch-points are allowed. So a
282 // simple loop should be sufficient.
283 for (unsigned i = 0; i < web_event->touchesLength; ++i) {
284 point = web_event->touches + i;
285 if (point->id == event.touch_id())
286 break;
287 point = NULL;
288 }
289 break;
290 }
291 default:
292 DLOG(WARNING) << "Unknown touch event " << event.type();
293 break;
294 }
295
296 if (!point)
297 return NULL;
298
299 // The spec requires the radii values to be positive (and 1 when unknown).
300 point->radiusX = std::max(1.f, event.radius_x());
301 point->radiusY = std::max(1.f, event.radius_y());
302 point->rotationAngle = event.rotation_angle();
303 point->force = event.force();
304
305 // Update the location and state of the point.
306 point->state = TouchPointStateFromEvent(event);
307 if (point->state == blink::WebTouchPoint::StateMoved) {
308 // It is possible for badly written touch drivers to emit Move events even
309 // when the touch location hasn't changed. In such cases, consume the event
310 // and pretend nothing happened.
311 if (point->position.x == event.x() && point->position.y == event.y())
312 return NULL;
313 }
314 point->position.x = event.x();
315 point->position.y = event.y();
316
317 const gfx::PointF& root_point = event.root_location_f();
318 point->screenPosition.x = root_point.x();
319 point->screenPosition.y = root_point.y();
320
321 // Mark the rest of the points as stationary.
322 for (unsigned i = 0; i < web_event->touchesLength; ++i) {
323 blink::WebTouchPoint* iter = web_event->touches + i;
324 if (iter != point)
325 iter->state = blink::WebTouchPoint::StateStationary;
326 }
327
328 // Update the type of the touch event.
329 WebTouchEventTraits::ResetType(TouchEventTypeFromEvent(event),
330 event.time_stamp().InSecondsF(),
331 web_event);
332 web_event->modifiers = EventFlagsToWebEventModifiers(event.flags());
333
334 return point;
335 }
336
337 } // namespace content
338