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 #include "content/common/input/web_input_event_traits.h" 6 7 #include <bitset> 8 #include <limits> 9 10 #include "base/logging.h" 11 12 using blink::WebGestureEvent; 13 using blink::WebInputEvent; 14 using blink::WebKeyboardEvent; 15 using blink::WebMouseEvent; 16 using blink::WebMouseWheelEvent; 17 using blink::WebTouchEvent; 18 using std::numeric_limits; 19 20 namespace content { 21 namespace { 22 23 const int kInvalidTouchIndex = -1; 24 25 bool CanCoalesce(const WebKeyboardEvent& event_to_coalesce, 26 const WebKeyboardEvent& event) { 27 return false; 28 } 29 30 void Coalesce(const WebKeyboardEvent& event_to_coalesce, 31 WebKeyboardEvent* event) { 32 DCHECK(CanCoalesce(event_to_coalesce, *event)); 33 } 34 35 bool CanCoalesce(const WebMouseEvent& event_to_coalesce, 36 const WebMouseEvent& event) { 37 return event.type == event_to_coalesce.type && 38 event.type == WebInputEvent::MouseMove; 39 } 40 41 void Coalesce(const WebMouseEvent& event_to_coalesce, WebMouseEvent* event) { 42 DCHECK(CanCoalesce(event_to_coalesce, *event)); 43 // Accumulate movement deltas. 44 int x = event->movementX; 45 int y = event->movementY; 46 *event = event_to_coalesce; 47 event->movementX += x; 48 event->movementY += y; 49 } 50 51 bool CanCoalesce(const WebMouseWheelEvent& event_to_coalesce, 52 const WebMouseWheelEvent& event) { 53 return event.modifiers == event_to_coalesce.modifiers && 54 event.scrollByPage == event_to_coalesce.scrollByPage && 55 event.phase == event_to_coalesce.phase && 56 event.momentumPhase == event_to_coalesce.momentumPhase && 57 event.hasPreciseScrollingDeltas == 58 event_to_coalesce.hasPreciseScrollingDeltas; 59 } 60 61 float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) { 62 return accelerated_delta * acceleration_ratio; 63 } 64 65 float GetAccelerationRatio(float accelerated_delta, float unaccelerated_delta) { 66 if (unaccelerated_delta == 0.f || accelerated_delta == 0.f) 67 return 1.f; 68 return unaccelerated_delta / accelerated_delta; 69 } 70 71 void Coalesce(const WebMouseWheelEvent& event_to_coalesce, 72 WebMouseWheelEvent* event) { 73 DCHECK(CanCoalesce(event_to_coalesce, *event)); 74 float unaccelerated_x = 75 GetUnacceleratedDelta(event->deltaX, 76 event->accelerationRatioX) + 77 GetUnacceleratedDelta(event_to_coalesce.deltaX, 78 event_to_coalesce.accelerationRatioX); 79 float unaccelerated_y = 80 GetUnacceleratedDelta(event->deltaY, 81 event->accelerationRatioY) + 82 GetUnacceleratedDelta(event_to_coalesce.deltaY, 83 event_to_coalesce.accelerationRatioY); 84 event->deltaX += event_to_coalesce.deltaX; 85 event->deltaY += event_to_coalesce.deltaY; 86 event->wheelTicksX += event_to_coalesce.wheelTicksX; 87 event->wheelTicksY += event_to_coalesce.wheelTicksY; 88 event->accelerationRatioX = 89 GetAccelerationRatio(event->deltaX, unaccelerated_x); 90 event->accelerationRatioY = 91 GetAccelerationRatio(event->deltaY, unaccelerated_y); 92 DCHECK_GE(event_to_coalesce.timeStampSeconds, event->timeStampSeconds); 93 event->timeStampSeconds = event_to_coalesce.timeStampSeconds; 94 } 95 96 // Returns |kInvalidTouchIndex| iff |event| lacks a touch with an ID of |id|. 97 int GetIndexOfTouchID(const WebTouchEvent& event, int id) { 98 for (unsigned i = 0; i < event.touchesLength; ++i) { 99 if (event.touches[i].id == id) 100 return i; 101 } 102 return kInvalidTouchIndex; 103 } 104 105 bool CanCoalesce(const WebTouchEvent& event_to_coalesce, 106 const WebTouchEvent& event) { 107 if (event.type != event_to_coalesce.type || 108 event.type != WebInputEvent::TouchMove || 109 event.modifiers != event_to_coalesce.modifiers || 110 event.touchesLength != event_to_coalesce.touchesLength || 111 event.touchesLength > WebTouchEvent::touchesLengthCap) 112 return false; 113 114 COMPILE_ASSERT(WebTouchEvent::touchesLengthCap <= sizeof(int32_t) * 8U, 115 suboptimal_touches_length_cap_size); 116 // Ensure that we have a 1-to-1 mapping of pointer ids between touches. 117 std::bitset<WebTouchEvent::touchesLengthCap> unmatched_event_touches( 118 (1 << event.touchesLength) - 1); 119 for (unsigned i = 0; i < event_to_coalesce.touchesLength; ++i) { 120 int event_touch_index = 121 GetIndexOfTouchID(event, event_to_coalesce.touches[i].id); 122 if (event_touch_index == kInvalidTouchIndex) 123 return false; 124 if (!unmatched_event_touches[event_touch_index]) 125 return false; 126 unmatched_event_touches[event_touch_index] = false; 127 } 128 return unmatched_event_touches.none(); 129 } 130 131 void Coalesce(const WebTouchEvent& event_to_coalesce, WebTouchEvent* event) { 132 DCHECK(CanCoalesce(event_to_coalesce, *event)); 133 // The WebTouchPoints include absolute position information. So it is 134 // sufficient to simply replace the previous event with the new event-> 135 // However, it is necessary to make sure that all the points have the 136 // correct state, i.e. the touch-points that moved in the last event, but 137 // didn't change in the current event, will have Stationary state. It is 138 // necessary to change them back to Moved state. 139 WebTouchEvent old_event = *event; 140 *event = event_to_coalesce; 141 for (unsigned i = 0; i < event->touchesLength; ++i) { 142 int i_old = GetIndexOfTouchID(old_event, event->touches[i].id); 143 if (old_event.touches[i_old].state == blink::WebTouchPoint::StateMoved) 144 event->touches[i].state = blink::WebTouchPoint::StateMoved; 145 } 146 } 147 148 bool CanCoalesce(const WebGestureEvent& event_to_coalesce, 149 const WebGestureEvent& event) { 150 if (event.type != event_to_coalesce.type || 151 event.sourceDevice != event_to_coalesce.sourceDevice || 152 event.modifiers != event_to_coalesce.modifiers) 153 return false; 154 155 if (event.type == WebInputEvent::GestureScrollUpdate) 156 return true; 157 158 // GesturePinchUpdate scales can be combined only if they share a focal point, 159 // e.g., with double-tap drag zoom. 160 if (event.type == WebInputEvent::GesturePinchUpdate && 161 event.x == event_to_coalesce.x && 162 event.y == event_to_coalesce.y) 163 return true; 164 165 return false; 166 } 167 168 void Coalesce(const WebGestureEvent& event_to_coalesce, 169 WebGestureEvent* event) { 170 DCHECK(CanCoalesce(event_to_coalesce, *event)); 171 if (event->type == WebInputEvent::GestureScrollUpdate) { 172 event->data.scrollUpdate.deltaX += 173 event_to_coalesce.data.scrollUpdate.deltaX; 174 event->data.scrollUpdate.deltaY += 175 event_to_coalesce.data.scrollUpdate.deltaY; 176 } else if (event->type == WebInputEvent::GesturePinchUpdate) { 177 event->data.pinchUpdate.scale *= event_to_coalesce.data.pinchUpdate.scale; 178 // Ensure the scale remains bounded above 0 and below Infinity so that 179 // we can reliably perform operations like log on the values. 180 if (event->data.pinchUpdate.scale < numeric_limits<float>::min()) 181 event->data.pinchUpdate.scale = numeric_limits<float>::min(); 182 else if (event->data.pinchUpdate.scale > numeric_limits<float>::max()) 183 event->data.pinchUpdate.scale = numeric_limits<float>::max(); 184 } 185 } 186 187 struct WebInputEventSize { 188 template <class EventType> 189 bool Execute(WebInputEvent::Type /* type */, size_t* type_size) const { 190 *type_size = sizeof(EventType); 191 return true; 192 } 193 }; 194 195 struct WebInputEventClone { 196 template <class EventType> 197 bool Execute(const WebInputEvent& event, 198 ScopedWebInputEvent* scoped_event) const { 199 DCHECK_EQ(sizeof(EventType), event.size); 200 *scoped_event = ScopedWebInputEvent( 201 new EventType(static_cast<const EventType&>(event))); 202 return true; 203 } 204 }; 205 206 struct WebInputEventDelete { 207 template <class EventType> 208 bool Execute(WebInputEvent* event, bool* /* dummy_var */) const { 209 if (!event) 210 return false; 211 DCHECK_EQ(sizeof(EventType), event->size); 212 delete static_cast<EventType*>(event); 213 return true; 214 } 215 }; 216 217 struct WebInputEventCanCoalesce { 218 template <class EventType> 219 bool Execute(const WebInputEvent& event_to_coalesce, 220 const WebInputEvent* event) const { 221 if (event_to_coalesce.type != event->type) 222 return false; 223 DCHECK_EQ(sizeof(EventType), event->size); 224 DCHECK_EQ(sizeof(EventType), event_to_coalesce.size); 225 return CanCoalesce(static_cast<const EventType&>(event_to_coalesce), 226 *static_cast<const EventType*>(event)); 227 } 228 }; 229 230 struct WebInputEventCoalesce { 231 template <class EventType> 232 bool Execute(const WebInputEvent& event_to_coalesce, 233 WebInputEvent* event) const { 234 Coalesce(static_cast<const EventType&>(event_to_coalesce), 235 static_cast<EventType*>(event)); 236 return true; 237 } 238 }; 239 240 template <typename Operator, typename ArgIn, typename ArgOut> 241 bool Apply(Operator op, 242 WebInputEvent::Type type, 243 const ArgIn& arg_in, 244 ArgOut* arg_out) { 245 if (WebInputEvent::isMouseEventType(type)) 246 return op.template Execute<WebMouseEvent>(arg_in, arg_out); 247 else if (type == WebInputEvent::MouseWheel) 248 return op.template Execute<WebMouseWheelEvent>(arg_in, arg_out); 249 else if (WebInputEvent::isKeyboardEventType(type)) 250 return op.template Execute<WebKeyboardEvent>(arg_in, arg_out); 251 else if (WebInputEvent::isTouchEventType(type)) 252 return op.template Execute<WebTouchEvent>(arg_in, arg_out); 253 else if (WebInputEvent::isGestureEventType(type)) 254 return op.template Execute<WebGestureEvent>(arg_in, arg_out); 255 256 NOTREACHED() << "Unknown webkit event type " << type; 257 return false; 258 } 259 260 } // namespace 261 262 const char* WebInputEventTraits::GetName(WebInputEvent::Type type) { 263 #define CASE_TYPE(t) case WebInputEvent::t: return #t 264 switch(type) { 265 CASE_TYPE(Undefined); 266 CASE_TYPE(MouseDown); 267 CASE_TYPE(MouseUp); 268 CASE_TYPE(MouseMove); 269 CASE_TYPE(MouseEnter); 270 CASE_TYPE(MouseLeave); 271 CASE_TYPE(ContextMenu); 272 CASE_TYPE(MouseWheel); 273 CASE_TYPE(RawKeyDown); 274 CASE_TYPE(KeyDown); 275 CASE_TYPE(KeyUp); 276 CASE_TYPE(Char); 277 CASE_TYPE(GestureScrollBegin); 278 CASE_TYPE(GestureScrollEnd); 279 CASE_TYPE(GestureScrollUpdate); 280 CASE_TYPE(GestureFlingStart); 281 CASE_TYPE(GestureFlingCancel); 282 CASE_TYPE(GestureShowPress); 283 CASE_TYPE(GestureTap); 284 CASE_TYPE(GestureTapUnconfirmed); 285 CASE_TYPE(GestureTapDown); 286 CASE_TYPE(GestureTapCancel); 287 CASE_TYPE(GestureDoubleTap); 288 CASE_TYPE(GestureTwoFingerTap); 289 CASE_TYPE(GestureLongPress); 290 CASE_TYPE(GestureLongTap); 291 CASE_TYPE(GesturePinchBegin); 292 CASE_TYPE(GesturePinchEnd); 293 CASE_TYPE(GesturePinchUpdate); 294 CASE_TYPE(TouchStart); 295 CASE_TYPE(TouchMove); 296 CASE_TYPE(TouchEnd); 297 CASE_TYPE(TouchCancel); 298 default: 299 // Must include default to let blink::WebInputEvent add new event types 300 // before they're added here. 301 DLOG(WARNING) << 302 "Unhandled WebInputEvent type in WebInputEventTraits::GetName.\n"; 303 break; 304 } 305 #undef CASE_TYPE 306 return ""; 307 } 308 309 size_t WebInputEventTraits::GetSize(WebInputEvent::Type type) { 310 size_t size = 0; 311 Apply(WebInputEventSize(), type, type, &size); 312 return size; 313 } 314 315 ScopedWebInputEvent WebInputEventTraits::Clone(const WebInputEvent& event) { 316 ScopedWebInputEvent scoped_event; 317 Apply(WebInputEventClone(), event.type, event, &scoped_event); 318 return scoped_event.Pass(); 319 } 320 321 void WebInputEventTraits::Delete(WebInputEvent* event) { 322 if (!event) 323 return; 324 bool dummy_var = false; 325 Apply(WebInputEventDelete(), event->type, event, &dummy_var); 326 } 327 328 bool WebInputEventTraits::CanCoalesce(const WebInputEvent& event_to_coalesce, 329 const WebInputEvent& event) { 330 // Early out before casting. 331 if (event_to_coalesce.type != event.type) 332 return false; 333 return Apply(WebInputEventCanCoalesce(), 334 event.type, 335 event_to_coalesce, 336 &event); 337 } 338 339 void WebInputEventTraits::Coalesce(const WebInputEvent& event_to_coalesce, 340 WebInputEvent* event) { 341 DCHECK(event); 342 Apply(WebInputEventCoalesce(), event->type, event_to_coalesce, event); 343 } 344 345 bool WebInputEventTraits::IgnoresAckDisposition(const WebInputEvent& event) { 346 switch (event.type) { 347 case WebInputEvent::MouseDown: 348 case WebInputEvent::MouseUp: 349 case WebInputEvent::MouseEnter: 350 case WebInputEvent::MouseLeave: 351 case WebInputEvent::ContextMenu: 352 case WebInputEvent::GestureScrollBegin: 353 case WebInputEvent::GestureScrollEnd: 354 case WebInputEvent::GestureShowPress: 355 case WebInputEvent::GestureTapUnconfirmed: 356 case WebInputEvent::GestureTapDown: 357 case WebInputEvent::GestureTapCancel: 358 case WebInputEvent::GesturePinchBegin: 359 case WebInputEvent::GesturePinchEnd: 360 case WebInputEvent::TouchCancel: 361 return true; 362 case WebInputEvent::TouchStart: 363 case WebInputEvent::TouchMove: 364 case WebInputEvent::TouchEnd: 365 return !static_cast<const WebTouchEvent&>(event).cancelable; 366 default: 367 return false; 368 } 369 } 370 371 } // namespace content 372