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