OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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/event_with_latency_info.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 float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) { |
| 26 return accelerated_delta * acceleration_ratio; |
| 27 } |
| 28 |
| 29 float GetAccelerationRatio(float accelerated_delta, float unaccelerated_delta) { |
| 30 if (unaccelerated_delta == 0.f || accelerated_delta == 0.f) |
| 31 return 1.f; |
| 32 return unaccelerated_delta / accelerated_delta; |
| 33 } |
| 34 |
| 35 // Returns |kInvalidTouchIndex| iff |event| lacks a touch with an ID of |id|. |
| 36 int GetIndexOfTouchID(const WebTouchEvent& event, int id) { |
| 37 for (unsigned i = 0; i < event.touchesLength; ++i) { |
| 38 if (event.touches[i].id == id) |
| 39 return i; |
| 40 } |
| 41 return kInvalidTouchIndex; |
| 42 } |
| 43 |
| 44 WebInputEvent::DispatchType MergeDispatchTypes( |
| 45 WebInputEvent::DispatchType type_1, |
| 46 WebInputEvent::DispatchType type_2) { |
| 47 static_assert(WebInputEvent::DispatchType::Blocking < |
| 48 WebInputEvent::DispatchType::EventNonBlocking, |
| 49 "Enum not ordered correctly"); |
| 50 static_assert(WebInputEvent::DispatchType::EventNonBlocking < |
| 51 WebInputEvent::DispatchType::ListenersNonBlockingPassive, |
| 52 "Enum not ordered correctly"); |
| 53 static_assert( |
| 54 WebInputEvent::DispatchType::ListenersNonBlockingPassive < |
| 55 WebInputEvent::DispatchType::ListenersForcedNonBlockingPassive, |
| 56 "Enum not ordered correctly"); |
| 57 return static_cast<WebInputEvent::DispatchType>( |
| 58 std::min(static_cast<int>(type_1), static_cast<int>(type_2))); |
| 59 } |
| 60 |
| 61 bool CanCoalesce(const blink::WebMouseEvent& event_to_coalesce, |
| 62 const blink::WebMouseEvent& event) { |
| 63 return event.type == event_to_coalesce.type && |
| 64 event.type == WebInputEvent::MouseMove; |
| 65 } |
| 66 |
| 67 void Coalesce(const WebMouseEvent& event_to_coalesce, WebMouseEvent* event) { |
| 68 DCHECK(CanCoalesce(event_to_coalesce, *event)); |
| 69 // Accumulate movement deltas. |
| 70 int x = event->movementX; |
| 71 int y = event->movementY; |
| 72 *event = event_to_coalesce; |
| 73 event->movementX += x; |
| 74 event->movementY += y; |
| 75 } |
| 76 |
| 77 bool CanCoalesce(const WebMouseWheelEvent& event_to_coalesce, |
| 78 const WebMouseWheelEvent& event) { |
| 79 return event.modifiers == event_to_coalesce.modifiers && |
| 80 event.scrollByPage == event_to_coalesce.scrollByPage && |
| 81 event.phase == event_to_coalesce.phase && |
| 82 event.momentumPhase == event_to_coalesce.momentumPhase && |
| 83 event.hasPreciseScrollingDeltas == |
| 84 event_to_coalesce.hasPreciseScrollingDeltas; |
| 85 } |
| 86 |
| 87 void Coalesce(const WebMouseWheelEvent& event_to_coalesce, |
| 88 WebMouseWheelEvent* event) { |
| 89 DCHECK(CanCoalesce(event_to_coalesce, *event)); |
| 90 float unaccelerated_x = |
| 91 GetUnacceleratedDelta(event->deltaX, event->accelerationRatioX) + |
| 92 GetUnacceleratedDelta(event_to_coalesce.deltaX, |
| 93 event_to_coalesce.accelerationRatioX); |
| 94 float unaccelerated_y = |
| 95 GetUnacceleratedDelta(event->deltaY, event->accelerationRatioY) + |
| 96 GetUnacceleratedDelta(event_to_coalesce.deltaY, |
| 97 event_to_coalesce.accelerationRatioY); |
| 98 event->deltaX += event_to_coalesce.deltaX; |
| 99 event->deltaY += event_to_coalesce.deltaY; |
| 100 event->wheelTicksX += event_to_coalesce.wheelTicksX; |
| 101 event->wheelTicksY += event_to_coalesce.wheelTicksY; |
| 102 event->accelerationRatioX = |
| 103 GetAccelerationRatio(event->deltaX, unaccelerated_x); |
| 104 event->accelerationRatioY = |
| 105 GetAccelerationRatio(event->deltaY, unaccelerated_y); |
| 106 } |
| 107 |
| 108 bool CanCoalesce(const WebTouchEvent& event_to_coalesce, |
| 109 const WebTouchEvent& event) { |
| 110 if (event.type != event_to_coalesce.type || |
| 111 event.type != WebInputEvent::TouchMove || |
| 112 event.modifiers != event_to_coalesce.modifiers || |
| 113 event.touchesLength != event_to_coalesce.touchesLength || |
| 114 event.touchesLength > WebTouchEvent::touchesLengthCap) |
| 115 return false; |
| 116 |
| 117 static_assert(WebTouchEvent::touchesLengthCap <= sizeof(int32_t) * 8U, |
| 118 "suboptimal touchesLengthCap size"); |
| 119 // Ensure that we have a 1-to-1 mapping of pointer ids between touches. |
| 120 std::bitset<WebTouchEvent::touchesLengthCap> unmatched_event_touches( |
| 121 (1 << event.touchesLength) - 1); |
| 122 for (unsigned i = 0; i < event_to_coalesce.touchesLength; ++i) { |
| 123 int event_touch_index = |
| 124 GetIndexOfTouchID(event, event_to_coalesce.touches[i].id); |
| 125 if (event_touch_index == kInvalidTouchIndex) |
| 126 return false; |
| 127 if (!unmatched_event_touches[event_touch_index]) |
| 128 return false; |
| 129 unmatched_event_touches[event_touch_index] = false; |
| 130 } |
| 131 return unmatched_event_touches.none(); |
| 132 } |
| 133 |
| 134 void Coalesce(const WebTouchEvent& event_to_coalesce, WebTouchEvent* event) { |
| 135 DCHECK(CanCoalesce(event_to_coalesce, *event)); |
| 136 // The WebTouchPoints include absolute position information. So it is |
| 137 // sufficient to simply replace the previous event with the new event-> |
| 138 // However, it is necessary to make sure that all the points have the |
| 139 // correct state, i.e. the touch-points that moved in the last event, but |
| 140 // didn't change in the current event, will have Stationary state. It is |
| 141 // necessary to change them back to Moved state. |
| 142 WebTouchEvent old_event = *event; |
| 143 *event = event_to_coalesce; |
| 144 for (unsigned i = 0; i < event->touchesLength; ++i) { |
| 145 int i_old = GetIndexOfTouchID(old_event, event->touches[i].id); |
| 146 if (old_event.touches[i_old].state == blink::WebTouchPoint::StateMoved) |
| 147 event->touches[i].state = blink::WebTouchPoint::StateMoved; |
| 148 } |
| 149 event->movedBeyondSlopRegion |= old_event.movedBeyondSlopRegion; |
| 150 event->dispatchType = MergeDispatchTypes(old_event.dispatchType, |
| 151 event_to_coalesce.dispatchType); |
| 152 } |
| 153 |
| 154 bool CanCoalesce(const WebGestureEvent& event_to_coalesce, |
| 155 const WebGestureEvent& event) { |
| 156 if (event.type != event_to_coalesce.type || |
| 157 event.sourceDevice != event_to_coalesce.sourceDevice || |
| 158 event.modifiers != event_to_coalesce.modifiers) |
| 159 return false; |
| 160 |
| 161 if (event.type == WebInputEvent::GestureScrollUpdate) |
| 162 return true; |
| 163 |
| 164 // GesturePinchUpdate scales can be combined only if they share a focal point, |
| 165 // e.g., with double-tap drag zoom. |
| 166 if (event.type == WebInputEvent::GesturePinchUpdate && |
| 167 event.x == event_to_coalesce.x && event.y == event_to_coalesce.y) |
| 168 return true; |
| 169 |
| 170 return false; |
| 171 } |
| 172 |
| 173 void Coalesce(const WebGestureEvent& event_to_coalesce, |
| 174 WebGestureEvent* event) { |
| 175 DCHECK(CanCoalesce(event_to_coalesce, *event)); |
| 176 if (event->type == WebInputEvent::GestureScrollUpdate) { |
| 177 event->data.scrollUpdate.deltaX += |
| 178 event_to_coalesce.data.scrollUpdate.deltaX; |
| 179 event->data.scrollUpdate.deltaY += |
| 180 event_to_coalesce.data.scrollUpdate.deltaY; |
| 181 DCHECK_EQ( |
| 182 event->data.scrollUpdate.previousUpdateInSequencePrevented, |
| 183 event_to_coalesce.data.scrollUpdate.previousUpdateInSequencePrevented); |
| 184 } else if (event->type == WebInputEvent::GesturePinchUpdate) { |
| 185 event->data.pinchUpdate.scale *= event_to_coalesce.data.pinchUpdate.scale; |
| 186 // Ensure the scale remains bounded above 0 and below Infinity so that |
| 187 // we can reliably perform operations like log on the values. |
| 188 if (event->data.pinchUpdate.scale < numeric_limits<float>::min()) |
| 189 event->data.pinchUpdate.scale = numeric_limits<float>::min(); |
| 190 else if (event->data.pinchUpdate.scale > numeric_limits<float>::max()) |
| 191 event->data.pinchUpdate.scale = numeric_limits<float>::max(); |
| 192 } |
| 193 } |
| 194 |
| 195 } // namespace |
| 196 |
| 197 template <class T> |
| 198 bool EventWithLatencyInfo<T>::CanCoalesceWith( |
| 199 const EventWithLatencyInfo& other) const { |
| 200 if (other.event.type != event.type) |
| 201 return false; |
| 202 |
| 203 DCHECK_EQ(sizeof(T), event.size); |
| 204 DCHECK_EQ(sizeof(T), other.event.size); |
| 205 |
| 206 return CanCoalesce(other.event, event); |
| 207 } |
| 208 |
| 209 template <class T> |
| 210 void EventWithLatencyInfo<T>::CoalesceWith(const EventWithLatencyInfo& other) { |
| 211 // |other| should be a newer event than |this|. |
| 212 if (other.latency.trace_id() >= 0 && latency.trace_id() >= 0) |
| 213 DCHECK_GT(other.latency.trace_id(), latency.trace_id()); |
| 214 |
| 215 // New events get coalesced into older events, and the newer timestamp |
| 216 // should always be preserved. |
| 217 const double time_stamp_seconds = other.event.timeStampSeconds; |
| 218 Coalesce(other.event, &event); |
| 219 event.timeStampSeconds = time_stamp_seconds; |
| 220 |
| 221 // When coalescing two input events, we keep the oldest LatencyInfo |
| 222 // for Telemetry latency tests, since it will represent the longest |
| 223 // latency. |
| 224 other.latency = latency; |
| 225 other.latency.set_coalesced(); |
| 226 } |
| 227 |
| 228 // Explicit instantiations. |
| 229 template class EXPORT_TEMPLATE_DEFINE(CONTENT_EXPORT) |
| 230 EventWithLatencyInfo<blink::WebGestureEvent>; |
| 231 template class EXPORT_TEMPLATE_DEFINE(CONTENT_EXPORT) |
| 232 EventWithLatencyInfo<blink::WebMouseWheelEvent>; |
| 233 template class EXPORT_TEMPLATE_DEFINE(CONTENT_EXPORT) |
| 234 EventWithLatencyInfo<blink::WebMouseEvent>; |
| 235 template class EXPORT_TEMPLATE_DEFINE(CONTENT_EXPORT) |
| 236 EventWithLatencyInfo<blink::WebTouchEvent>; |
| 237 |
| 238 } // namespace content |
OLD | NEW |