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/base/events/event.h" 9 #include "ui/base/events/event_constants.h" 10 11 namespace { 12 13 int WebModifiersToUIFlags(int modifiers) { 14 int flags = ui::EF_NONE; 15 16 if (modifiers & WebKit::WebInputEvent::ShiftKey) 17 flags |= ui::EF_SHIFT_DOWN; 18 if (modifiers & WebKit::WebInputEvent::ControlKey) 19 flags |= ui::EF_CONTROL_DOWN; 20 if (modifiers & WebKit::WebInputEvent::AltKey) 21 flags |= ui::EF_ALT_DOWN; 22 23 if (modifiers & WebKit::WebInputEvent::LeftButtonDown) 24 flags |= ui::EF_LEFT_MOUSE_BUTTON; 25 if (modifiers & WebKit::WebInputEvent::RightButtonDown) 26 flags |= ui::EF_RIGHT_MOUSE_BUTTON; 27 if (modifiers & WebKit::WebInputEvent::MiddleButtonDown) 28 flags |= ui::EF_MIDDLE_MOUSE_BUTTON; 29 30 if (modifiers & WebKit::WebInputEvent::CapsLockOn) 31 flags |= ui::EF_CAPS_LOCK_DOWN; 32 33 return flags; 34 } 35 36 ui::EventType WebTouchPointStateToEventType( 37 WebKit::WebTouchPoint::State state) { 38 switch (state) { 39 case WebKit::WebTouchPoint::StateReleased: 40 return ui::ET_TOUCH_RELEASED; 41 42 case WebKit::WebTouchPoint::StatePressed: 43 return ui::ET_TOUCH_PRESSED; 44 45 case WebKit::WebTouchPoint::StateMoved: 46 return ui::ET_TOUCH_MOVED; 47 48 case WebKit::WebTouchPoint::StateCancelled: 49 return ui::ET_TOUCH_CANCELLED; 50 51 default: 52 return ui::ET_UNKNOWN; 53 } 54 } 55 56 WebKit::WebTouchPoint::State TouchPointStateFromEvent( 57 const ui::TouchEvent& event) { 58 switch (event.type()) { 59 case ui::ET_TOUCH_PRESSED: 60 return WebKit::WebTouchPoint::StatePressed; 61 case ui::ET_TOUCH_RELEASED: 62 return WebKit::WebTouchPoint::StateReleased; 63 case ui::ET_TOUCH_MOVED: 64 return WebKit::WebTouchPoint::StateMoved; 65 case ui::ET_TOUCH_CANCELLED: 66 return WebKit::WebTouchPoint::StateCancelled; 67 default: 68 return WebKit::WebTouchPoint::StateUndefined; 69 } 70 } 71 72 WebKit::WebInputEvent::Type TouchEventTypeFromEvent( 73 const ui::TouchEvent& event) { 74 switch (event.type()) { 75 case ui::ET_TOUCH_PRESSED: 76 return WebKit::WebInputEvent::TouchStart; 77 case ui::ET_TOUCH_RELEASED: 78 return WebKit::WebInputEvent::TouchEnd; 79 case ui::ET_TOUCH_MOVED: 80 return WebKit::WebInputEvent::TouchMove; 81 case ui::ET_TOUCH_CANCELLED: 82 return WebKit::WebInputEvent::TouchCancel; 83 default: 84 return WebKit::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 WebKit::WebTouchEvent& touch = touch_with_latency.event; 97 ui::EventType type = ui::ET_UNKNOWN; 98 switch (touch.type) { 99 case WebKit::WebInputEvent::TouchStart: 100 type = ui::ET_TOUCH_PRESSED; 101 break; 102 case WebKit::WebInputEvent::TouchEnd: 103 type = ui::ET_TOUCH_RELEASED; 104 break; 105 case WebKit::WebInputEvent::TouchMove: 106 type = ui::ET_TOUCH_MOVED; 107 break; 108 case WebKit::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 WebKit::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 WebKit::WebGestureEvent MakeWebGestureEventFromUIEvent( 152 const ui::GestureEvent& event) { 153 WebKit::WebGestureEvent gesture_event; 154 155 switch (event.type()) { 156 case ui::ET_GESTURE_TAP: 157 gesture_event.type = WebKit::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 = WebKit::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_TAP_CANCEL: 170 gesture_event.type = WebKit::WebInputEvent::GestureTapCancel; 171 break; 172 case ui::ET_GESTURE_SCROLL_BEGIN: 173 gesture_event.type = WebKit::WebInputEvent::GestureScrollBegin; 174 break; 175 case ui::ET_GESTURE_SCROLL_UPDATE: 176 gesture_event.type = WebKit::WebInputEvent::GestureScrollUpdate; 177 gesture_event.data.scrollUpdate.deltaX = event.details().scroll_x(); 178 gesture_event.data.scrollUpdate.deltaY = event.details().scroll_y(); 179 break; 180 case ui::ET_GESTURE_SCROLL_END: 181 gesture_event.type = WebKit::WebInputEvent::GestureScrollEnd; 182 break; 183 case ui::ET_GESTURE_PINCH_BEGIN: 184 gesture_event.type = WebKit::WebInputEvent::GesturePinchBegin; 185 break; 186 case ui::ET_GESTURE_PINCH_UPDATE: 187 gesture_event.type = WebKit::WebInputEvent::GesturePinchUpdate; 188 gesture_event.data.pinchUpdate.scale = event.details().scale(); 189 break; 190 case ui::ET_GESTURE_PINCH_END: 191 gesture_event.type = WebKit::WebInputEvent::GesturePinchEnd; 192 break; 193 case ui::ET_SCROLL_FLING_START: 194 gesture_event.type = WebKit::WebInputEvent::GestureFlingStart; 195 gesture_event.data.flingStart.velocityX = event.details().velocity_x(); 196 gesture_event.data.flingStart.velocityY = event.details().velocity_y(); 197 break; 198 case ui::ET_SCROLL_FLING_CANCEL: 199 gesture_event.type = WebKit::WebInputEvent::GestureFlingCancel; 200 break; 201 case ui::ET_GESTURE_LONG_PRESS: 202 gesture_event.type = WebKit::WebInputEvent::GestureLongPress; 203 gesture_event.data.longPress.width = 204 event.details().bounding_box().width(); 205 gesture_event.data.longPress.height = 206 event.details().bounding_box().height(); 207 break; 208 case ui::ET_GESTURE_LONG_TAP: 209 gesture_event.type = WebKit::WebInputEvent::GestureLongTap; 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_TWO_FINGER_TAP: 216 gesture_event.type = WebKit::WebInputEvent::GestureTwoFingerTap; 217 gesture_event.data.twoFingerTap.firstFingerWidth = 218 event.details().first_finger_width(); 219 gesture_event.data.twoFingerTap.firstFingerHeight = 220 event.details().first_finger_height(); 221 break; 222 case ui::ET_GESTURE_BEGIN: 223 case ui::ET_GESTURE_END: 224 case ui::ET_GESTURE_MULTIFINGER_SWIPE: 225 gesture_event.type = WebKit::WebInputEvent::Undefined; 226 break; 227 default: 228 NOTREACHED() << "Unknown gesture type: " << event.type(); 229 } 230 231 gesture_event.sourceDevice = WebKit::WebGestureEvent::Touchscreen; 232 gesture_event.modifiers = EventFlagsToWebEventModifiers(event.flags()); 233 gesture_event.timeStampSeconds = event.time_stamp().InSecondsF(); 234 235 return gesture_event; 236 } 237 238 int EventFlagsToWebEventModifiers(int flags) { 239 int modifiers = 0; 240 241 if (flags & ui::EF_SHIFT_DOWN) 242 modifiers |= WebKit::WebInputEvent::ShiftKey; 243 if (flags & ui::EF_CONTROL_DOWN) 244 modifiers |= WebKit::WebInputEvent::ControlKey; 245 if (flags & ui::EF_ALT_DOWN) 246 modifiers |= WebKit::WebInputEvent::AltKey; 247 // TODO(beng): MetaKey/META_MASK 248 if (flags & ui::EF_LEFT_MOUSE_BUTTON) 249 modifiers |= WebKit::WebInputEvent::LeftButtonDown; 250 if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) 251 modifiers |= WebKit::WebInputEvent::MiddleButtonDown; 252 if (flags & ui::EF_RIGHT_MOUSE_BUTTON) 253 modifiers |= WebKit::WebInputEvent::RightButtonDown; 254 if (flags & ui::EF_CAPS_LOCK_DOWN) 255 modifiers |= WebKit::WebInputEvent::CapsLockOn; 256 return modifiers; 257 } 258 259 WebKit::WebTouchPoint* UpdateWebTouchEventFromUIEvent( 260 const ui::TouchEvent& event, 261 WebKit::WebTouchEvent* web_event) { 262 WebKit::WebTouchPoint* point = NULL; 263 switch (event.type()) { 264 case ui::ET_TOUCH_PRESSED: 265 // Add a new touch point. 266 if (web_event->touchesLength < WebKit::WebTouchEvent::touchesLengthCap) { 267 point = &web_event->touches[web_event->touchesLength++]; 268 point->id = event.touch_id(); 269 } 270 break; 271 case ui::ET_TOUCH_RELEASED: 272 case ui::ET_TOUCH_CANCELLED: 273 case ui::ET_TOUCH_MOVED: { 274 // The touch point should have been added to the event from an earlier 275 // _PRESSED event. So find that. 276 // At the moment, only a maximum of 4 touch-points are allowed. So a 277 // simple loop should be sufficient. 278 for (unsigned i = 0; i < web_event->touchesLength; ++i) { 279 point = web_event->touches + i; 280 if (point->id == event.touch_id()) 281 break; 282 point = NULL; 283 } 284 break; 285 } 286 default: 287 DLOG(WARNING) << "Unknown touch event " << event.type(); 288 break; 289 } 290 291 if (!point) 292 return NULL; 293 294 // The spec requires the radii values to be positive (and 1 when unknown). 295 point->radiusX = std::max(1.f, event.radius_x()); 296 point->radiusY = std::max(1.f, event.radius_y()); 297 point->rotationAngle = event.rotation_angle(); 298 point->force = event.force(); 299 300 // Update the location and state of the point. 301 point->state = TouchPointStateFromEvent(event); 302 if (point->state == WebKit::WebTouchPoint::StateMoved) { 303 // It is possible for badly written touch drivers to emit Move events even 304 // when the touch location hasn't changed. In such cases, consume the event 305 // and pretend nothing happened. 306 if (point->position.x == event.x() && point->position.y == event.y()) 307 return NULL; 308 } 309 point->position.x = event.x(); 310 point->position.y = event.y(); 311 312 const gfx::Point root_point = event.root_location(); 313 point->screenPosition.x = root_point.x(); 314 point->screenPosition.y = root_point.y(); 315 316 // Mark the rest of the points as stationary. 317 for (unsigned i = 0; i < web_event->touchesLength; ++i) { 318 WebKit::WebTouchPoint* iter = web_event->touches + i; 319 if (iter != point) 320 iter->state = WebKit::WebTouchPoint::StateStationary; 321 } 322 323 // Update the type of the touch event. 324 web_event->type = TouchEventTypeFromEvent(event); 325 web_event->timeStampSeconds = event.time_stamp().InSecondsF(); 326 web_event->modifiers = EventFlagsToWebEventModifiers(event.flags()); 327 328 return point; 329 } 330 331 } // namespace content 332