OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/common/input/web_input_event_traits.h" | 5 #include "content/common/input/web_input_event_traits.h" |
6 | 6 |
7 #include <bitset> | |
8 #include <limits> | |
9 | |
10 #include "base/logging.h" | 7 #include "base/logging.h" |
11 #include "base/strings/stringprintf.h" | 8 #include "base/strings/stringprintf.h" |
12 | 9 |
13 using base::StringAppendF; | 10 using base::StringAppendF; |
14 using base::SStringPrintf; | 11 using base::SStringPrintf; |
15 using blink::WebGestureEvent; | 12 using blink::WebGestureEvent; |
16 using blink::WebInputEvent; | 13 using blink::WebInputEvent; |
17 using blink::WebKeyboardEvent; | 14 using blink::WebKeyboardEvent; |
18 using blink::WebMouseEvent; | 15 using blink::WebMouseEvent; |
19 using blink::WebMouseWheelEvent; | 16 using blink::WebMouseWheelEvent; |
20 using blink::WebTouchEvent; | 17 using blink::WebTouchEvent; |
21 using blink::WebTouchPoint; | 18 using blink::WebTouchPoint; |
22 using std::numeric_limits; | |
23 | 19 |
24 namespace content { | 20 namespace content { |
25 namespace { | 21 namespace { |
26 | 22 |
27 const int kInvalidTouchIndex = -1; | |
28 | |
29 void ApppendEventDetails(const WebKeyboardEvent& event, std::string* result) { | 23 void ApppendEventDetails(const WebKeyboardEvent& event, std::string* result) { |
30 StringAppendF(result, | 24 StringAppendF(result, |
31 "{\n WinCode: %d\n NativeCode: %d\n IsSystem: %d\n" | 25 "{\n WinCode: %d\n NativeCode: %d\n IsSystem: %d\n" |
32 " Text: %s\n UnmodifiedText: %s\n KeyIdentifier: %s\n}", | 26 " Text: %s\n UnmodifiedText: %s\n KeyIdentifier: %s\n}", |
33 event.windowsKeyCode, | 27 event.windowsKeyCode, |
34 event.nativeKeyCode, | 28 event.nativeKeyCode, |
35 event.isSystemKey, | 29 event.isSystemKey, |
36 reinterpret_cast<const char*>(event.text), | 30 reinterpret_cast<const char*>(event.text), |
37 reinterpret_cast<const char*>(event.unmodifiedText), | 31 reinterpret_cast<const char*>(event.unmodifiedText), |
38 reinterpret_cast<const char*>(event.keyIdentifier)); | 32 reinterpret_cast<const char*>(event.keyIdentifier)); |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
105 StringAppendF(result, | 99 StringAppendF(result, |
106 "{\n Touches: %u, DispatchType: %d, CausesScrolling: %d," | 100 "{\n Touches: %u, DispatchType: %d, CausesScrolling: %d," |
107 " uniqueTouchEventId: %u\n[\n", | 101 " uniqueTouchEventId: %u\n[\n", |
108 event.touchesLength, event.dispatchType, | 102 event.touchesLength, event.dispatchType, |
109 event.movedBeyondSlopRegion, event.uniqueTouchEventId); | 103 event.movedBeyondSlopRegion, event.uniqueTouchEventId); |
110 for (unsigned i = 0; i < event.touchesLength; ++i) | 104 for (unsigned i = 0; i < event.touchesLength; ++i) |
111 ApppendTouchPointDetails(event.touches[i], result); | 105 ApppendTouchPointDetails(event.touches[i], result); |
112 result->append(" ]\n}"); | 106 result->append(" ]\n}"); |
113 } | 107 } |
114 | 108 |
115 bool CanCoalesce(const WebKeyboardEvent& event_to_coalesce, | |
116 const WebKeyboardEvent& event) { | |
117 return false; | |
118 } | |
119 | |
120 void Coalesce(const WebKeyboardEvent& event_to_coalesce, | |
121 WebKeyboardEvent* event) { | |
122 DCHECK(CanCoalesce(event_to_coalesce, *event)); | |
123 } | |
124 | |
125 bool CanCoalesce(const WebMouseEvent& event_to_coalesce, | |
126 const WebMouseEvent& event) { | |
127 return event.type == event_to_coalesce.type && | |
128 event.type == WebInputEvent::MouseMove; | |
129 } | |
130 | |
131 void Coalesce(const WebMouseEvent& event_to_coalesce, WebMouseEvent* event) { | |
132 DCHECK(CanCoalesce(event_to_coalesce, *event)); | |
133 // Accumulate movement deltas. | |
134 int x = event->movementX; | |
135 int y = event->movementY; | |
136 *event = event_to_coalesce; | |
137 event->movementX += x; | |
138 event->movementY += y; | |
139 } | |
140 | |
141 bool CanCoalesce(const WebMouseWheelEvent& event_to_coalesce, | |
142 const WebMouseWheelEvent& event) { | |
143 return event.modifiers == event_to_coalesce.modifiers && | |
144 event.scrollByPage == event_to_coalesce.scrollByPage && | |
145 event.phase == event_to_coalesce.phase && | |
146 event.momentumPhase == event_to_coalesce.momentumPhase && | |
147 event.hasPreciseScrollingDeltas == | |
148 event_to_coalesce.hasPreciseScrollingDeltas; | |
149 } | |
150 | |
151 float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) { | |
152 return accelerated_delta * acceleration_ratio; | |
153 } | |
154 | |
155 float GetAccelerationRatio(float accelerated_delta, float unaccelerated_delta) { | |
156 if (unaccelerated_delta == 0.f || accelerated_delta == 0.f) | |
157 return 1.f; | |
158 return unaccelerated_delta / accelerated_delta; | |
159 } | |
160 | |
161 void Coalesce(const WebMouseWheelEvent& event_to_coalesce, | |
162 WebMouseWheelEvent* event) { | |
163 DCHECK(CanCoalesce(event_to_coalesce, *event)); | |
164 float unaccelerated_x = | |
165 GetUnacceleratedDelta(event->deltaX, | |
166 event->accelerationRatioX) + | |
167 GetUnacceleratedDelta(event_to_coalesce.deltaX, | |
168 event_to_coalesce.accelerationRatioX); | |
169 float unaccelerated_y = | |
170 GetUnacceleratedDelta(event->deltaY, | |
171 event->accelerationRatioY) + | |
172 GetUnacceleratedDelta(event_to_coalesce.deltaY, | |
173 event_to_coalesce.accelerationRatioY); | |
174 event->deltaX += event_to_coalesce.deltaX; | |
175 event->deltaY += event_to_coalesce.deltaY; | |
176 event->wheelTicksX += event_to_coalesce.wheelTicksX; | |
177 event->wheelTicksY += event_to_coalesce.wheelTicksY; | |
178 event->accelerationRatioX = | |
179 GetAccelerationRatio(event->deltaX, unaccelerated_x); | |
180 event->accelerationRatioY = | |
181 GetAccelerationRatio(event->deltaY, unaccelerated_y); | |
182 } | |
183 | |
184 // Returns |kInvalidTouchIndex| iff |event| lacks a touch with an ID of |id|. | |
185 int GetIndexOfTouchID(const WebTouchEvent& event, int id) { | |
186 for (unsigned i = 0; i < event.touchesLength; ++i) { | |
187 if (event.touches[i].id == id) | |
188 return i; | |
189 } | |
190 return kInvalidTouchIndex; | |
191 } | |
192 | |
193 WebInputEvent::DispatchType MergeDispatchTypes( | |
194 WebInputEvent::DispatchType type_1, | |
195 WebInputEvent::DispatchType type_2) { | |
196 static_assert(WebInputEvent::DispatchType::Blocking < | |
197 WebInputEvent::DispatchType::EventNonBlocking, | |
198 "Enum not ordered correctly"); | |
199 static_assert(WebInputEvent::DispatchType::EventNonBlocking < | |
200 WebInputEvent::DispatchType::ListenersNonBlockingPassive, | |
201 "Enum not ordered correctly"); | |
202 static_assert( | |
203 WebInputEvent::DispatchType::ListenersNonBlockingPassive < | |
204 WebInputEvent::DispatchType::ListenersForcedNonBlockingPassive, | |
205 "Enum not ordered correctly"); | |
206 return static_cast<WebInputEvent::DispatchType>( | |
207 std::min(static_cast<int>(type_1), static_cast<int>(type_2))); | |
208 } | |
209 | |
210 bool CanCoalesce(const WebTouchEvent& event_to_coalesce, | |
211 const WebTouchEvent& event) { | |
212 if (event.type != event_to_coalesce.type || | |
213 event.type != WebInputEvent::TouchMove || | |
214 event.modifiers != event_to_coalesce.modifiers || | |
215 event.touchesLength != event_to_coalesce.touchesLength || | |
216 event.touchesLength > WebTouchEvent::touchesLengthCap) | |
217 return false; | |
218 | |
219 static_assert(WebTouchEvent::touchesLengthCap <= sizeof(int32_t) * 8U, | |
220 "suboptimal touchesLengthCap size"); | |
221 // Ensure that we have a 1-to-1 mapping of pointer ids between touches. | |
222 std::bitset<WebTouchEvent::touchesLengthCap> unmatched_event_touches( | |
223 (1 << event.touchesLength) - 1); | |
224 for (unsigned i = 0; i < event_to_coalesce.touchesLength; ++i) { | |
225 int event_touch_index = | |
226 GetIndexOfTouchID(event, event_to_coalesce.touches[i].id); | |
227 if (event_touch_index == kInvalidTouchIndex) | |
228 return false; | |
229 if (!unmatched_event_touches[event_touch_index]) | |
230 return false; | |
231 unmatched_event_touches[event_touch_index] = false; | |
232 } | |
233 return unmatched_event_touches.none(); | |
234 } | |
235 | |
236 void Coalesce(const WebTouchEvent& event_to_coalesce, WebTouchEvent* event) { | |
237 DCHECK(CanCoalesce(event_to_coalesce, *event)); | |
238 // The WebTouchPoints include absolute position information. So it is | |
239 // sufficient to simply replace the previous event with the new event-> | |
240 // However, it is necessary to make sure that all the points have the | |
241 // correct state, i.e. the touch-points that moved in the last event, but | |
242 // didn't change in the current event, will have Stationary state. It is | |
243 // necessary to change them back to Moved state. | |
244 WebTouchEvent old_event = *event; | |
245 *event = event_to_coalesce; | |
246 for (unsigned i = 0; i < event->touchesLength; ++i) { | |
247 int i_old = GetIndexOfTouchID(old_event, event->touches[i].id); | |
248 if (old_event.touches[i_old].state == blink::WebTouchPoint::StateMoved) | |
249 event->touches[i].state = blink::WebTouchPoint::StateMoved; | |
250 } | |
251 event->movedBeyondSlopRegion |= old_event.movedBeyondSlopRegion; | |
252 event->dispatchType = MergeDispatchTypes(old_event.dispatchType, | |
253 event_to_coalesce.dispatchType); | |
254 } | |
255 | |
256 bool CanCoalesce(const WebGestureEvent& event_to_coalesce, | |
257 const WebGestureEvent& event) { | |
258 if (event.type != event_to_coalesce.type || | |
259 event.sourceDevice != event_to_coalesce.sourceDevice || | |
260 event.modifiers != event_to_coalesce.modifiers) | |
261 return false; | |
262 | |
263 if (event.type == WebInputEvent::GestureScrollUpdate) | |
264 return true; | |
265 | |
266 // GesturePinchUpdate scales can be combined only if they share a focal point, | |
267 // e.g., with double-tap drag zoom. | |
268 if (event.type == WebInputEvent::GesturePinchUpdate && | |
269 event.x == event_to_coalesce.x && | |
270 event.y == event_to_coalesce.y) | |
271 return true; | |
272 | |
273 return false; | |
274 } | |
275 | |
276 void Coalesce(const WebGestureEvent& event_to_coalesce, | |
277 WebGestureEvent* event) { | |
278 DCHECK(CanCoalesce(event_to_coalesce, *event)); | |
279 if (event->type == WebInputEvent::GestureScrollUpdate) { | |
280 event->data.scrollUpdate.deltaX += | |
281 event_to_coalesce.data.scrollUpdate.deltaX; | |
282 event->data.scrollUpdate.deltaY += | |
283 event_to_coalesce.data.scrollUpdate.deltaY; | |
284 DCHECK_EQ( | |
285 event->data.scrollUpdate.previousUpdateInSequencePrevented, | |
286 event_to_coalesce.data.scrollUpdate.previousUpdateInSequencePrevented); | |
287 } else if (event->type == WebInputEvent::GesturePinchUpdate) { | |
288 event->data.pinchUpdate.scale *= event_to_coalesce.data.pinchUpdate.scale; | |
289 // Ensure the scale remains bounded above 0 and below Infinity so that | |
290 // we can reliably perform operations like log on the values. | |
291 if (event->data.pinchUpdate.scale < numeric_limits<float>::min()) | |
292 event->data.pinchUpdate.scale = numeric_limits<float>::min(); | |
293 else if (event->data.pinchUpdate.scale > numeric_limits<float>::max()) | |
294 event->data.pinchUpdate.scale = numeric_limits<float>::max(); | |
295 } | |
296 } | |
297 | |
298 struct WebInputEventToString { | 109 struct WebInputEventToString { |
299 template <class EventType> | 110 template <class EventType> |
300 bool Execute(const WebInputEvent& event, std::string* result) const { | 111 bool Execute(const WebInputEvent& event, std::string* result) const { |
301 SStringPrintf(result, "%s (Time: %lf, Modifiers: %d)\n", | 112 SStringPrintf(result, "%s (Time: %lf, Modifiers: %d)\n", |
302 WebInputEventTraits::GetName(event.type), | 113 WebInputEventTraits::GetName(event.type), |
303 event.timeStampSeconds, | 114 event.timeStampSeconds, |
304 event.modifiers); | 115 event.modifiers); |
305 const EventType& typed_event = static_cast<const EventType&>(event); | 116 const EventType& typed_event = static_cast<const EventType&>(event); |
306 ApppendEventDetails(typed_event, result); | 117 ApppendEventDetails(typed_event, result); |
307 return true; | 118 return true; |
(...skipping 23 matching lines...) Expand all Loading... |
331 template <class EventType> | 142 template <class EventType> |
332 bool Execute(WebInputEvent* event, bool* /* dummy_var */) const { | 143 bool Execute(WebInputEvent* event, bool* /* dummy_var */) const { |
333 if (!event) | 144 if (!event) |
334 return false; | 145 return false; |
335 DCHECK_EQ(sizeof(EventType), event->size); | 146 DCHECK_EQ(sizeof(EventType), event->size); |
336 delete static_cast<EventType*>(event); | 147 delete static_cast<EventType*>(event); |
337 return true; | 148 return true; |
338 } | 149 } |
339 }; | 150 }; |
340 | 151 |
341 struct WebInputEventCanCoalesce { | |
342 template <class EventType> | |
343 bool Execute(const WebInputEvent& event_to_coalesce, | |
344 const WebInputEvent* event) const { | |
345 if (event_to_coalesce.type != event->type) | |
346 return false; | |
347 DCHECK_EQ(sizeof(EventType), event->size); | |
348 DCHECK_EQ(sizeof(EventType), event_to_coalesce.size); | |
349 return CanCoalesce(static_cast<const EventType&>(event_to_coalesce), | |
350 *static_cast<const EventType*>(event)); | |
351 } | |
352 }; | |
353 | |
354 struct WebInputEventCoalesce { | |
355 template <class EventType> | |
356 bool Execute(const WebInputEvent& event_to_coalesce, | |
357 WebInputEvent* event) const { | |
358 // New events get coalesced into older events, and the newer timestamp | |
359 // should always be preserved. | |
360 const double time_stamp_seconds = event_to_coalesce.timeStampSeconds; | |
361 Coalesce(static_cast<const EventType&>(event_to_coalesce), | |
362 static_cast<EventType*>(event)); | |
363 event->timeStampSeconds = time_stamp_seconds; | |
364 return true; | |
365 } | |
366 }; | |
367 | |
368 template <typename Operator, typename ArgIn, typename ArgOut> | 152 template <typename Operator, typename ArgIn, typename ArgOut> |
369 bool Apply(Operator op, | 153 bool Apply(Operator op, |
370 WebInputEvent::Type type, | 154 WebInputEvent::Type type, |
371 const ArgIn& arg_in, | 155 const ArgIn& arg_in, |
372 ArgOut* arg_out) { | 156 ArgOut* arg_out) { |
373 if (WebInputEvent::isMouseEventType(type)) | 157 if (WebInputEvent::isMouseEventType(type)) |
374 return op.template Execute<WebMouseEvent>(arg_in, arg_out); | 158 return op.template Execute<WebMouseEvent>(arg_in, arg_out); |
375 else if (type == WebInputEvent::MouseWheel) | 159 else if (type == WebInputEvent::MouseWheel) |
376 return op.template Execute<WebMouseWheelEvent>(arg_in, arg_out); | 160 return op.template Execute<WebMouseWheelEvent>(arg_in, arg_out); |
377 else if (WebInputEvent::isKeyboardEventType(type)) | 161 else if (WebInputEvent::isKeyboardEventType(type)) |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
453 return scoped_event; | 237 return scoped_event; |
454 } | 238 } |
455 | 239 |
456 void WebInputEventTraits::Delete(WebInputEvent* event) { | 240 void WebInputEventTraits::Delete(WebInputEvent* event) { |
457 if (!event) | 241 if (!event) |
458 return; | 242 return; |
459 bool dummy_var = false; | 243 bool dummy_var = false; |
460 Apply(WebInputEventDelete(), event->type, event, &dummy_var); | 244 Apply(WebInputEventDelete(), event->type, event, &dummy_var); |
461 } | 245 } |
462 | 246 |
463 bool WebInputEventTraits::CanCoalesce(const WebInputEvent& event_to_coalesce, | |
464 const WebInputEvent& event) { | |
465 // Early out before casting. | |
466 if (event_to_coalesce.type != event.type) | |
467 return false; | |
468 return Apply(WebInputEventCanCoalesce(), | |
469 event.type, | |
470 event_to_coalesce, | |
471 &event); | |
472 } | |
473 | |
474 void WebInputEventTraits::Coalesce(const WebInputEvent& event_to_coalesce, | |
475 WebInputEvent* event) { | |
476 DCHECK(event); | |
477 Apply(WebInputEventCoalesce(), event->type, event_to_coalesce, event); | |
478 } | |
479 | |
480 bool WebInputEventTraits::ShouldBlockEventStream(const WebInputEvent& event) { | 247 bool WebInputEventTraits::ShouldBlockEventStream(const WebInputEvent& event) { |
481 switch (event.type) { | 248 switch (event.type) { |
482 case WebInputEvent::MouseDown: | 249 case WebInputEvent::MouseDown: |
483 case WebInputEvent::MouseUp: | 250 case WebInputEvent::MouseUp: |
484 case WebInputEvent::MouseEnter: | 251 case WebInputEvent::MouseEnter: |
485 case WebInputEvent::MouseLeave: | 252 case WebInputEvent::MouseLeave: |
486 case WebInputEvent::ContextMenu: | 253 case WebInputEvent::ContextMenu: |
487 case WebInputEvent::GestureScrollBegin: | 254 case WebInputEvent::GestureScrollBegin: |
488 case WebInputEvent::GestureScrollEnd: | 255 case WebInputEvent::GestureScrollEnd: |
489 case WebInputEvent::GestureShowPress: | 256 case WebInputEvent::GestureShowPress: |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
530 | 297 |
531 uint32_t WebInputEventTraits::GetUniqueTouchEventId( | 298 uint32_t WebInputEventTraits::GetUniqueTouchEventId( |
532 const WebInputEvent& event) { | 299 const WebInputEvent& event) { |
533 if (WebInputEvent::isTouchEventType(event.type)) { | 300 if (WebInputEvent::isTouchEventType(event.type)) { |
534 return static_cast<const WebTouchEvent&>(event).uniqueTouchEventId; | 301 return static_cast<const WebTouchEvent&>(event).uniqueTouchEventId; |
535 } | 302 } |
536 return 0U; | 303 return 0U; |
537 } | 304 } |
538 | 305 |
539 } // namespace content | 306 } // namespace content |
OLD | NEW |