Index: content/common/input/event_with_latency_info.cc |
diff --git a/content/common/input/event_with_latency_info.cc b/content/common/input/event_with_latency_info.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c83d2332ceebab3bbdea89c42c554ba4bbd31db4 |
--- /dev/null |
+++ b/content/common/input/event_with_latency_info.cc |
@@ -0,0 +1,238 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/common/input/event_with_latency_info.h" |
+ |
+#include <bitset> |
+#include <limits> |
+ |
+#include "base/logging.h" |
+ |
+using blink::WebGestureEvent; |
+using blink::WebInputEvent; |
+using blink::WebKeyboardEvent; |
+using blink::WebMouseEvent; |
+using blink::WebMouseWheelEvent; |
+using blink::WebTouchEvent; |
+using std::numeric_limits; |
+ |
+namespace content { |
+namespace { |
+ |
+const int kInvalidTouchIndex = -1; |
+ |
+float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) { |
+ return accelerated_delta * acceleration_ratio; |
+} |
+ |
+float GetAccelerationRatio(float accelerated_delta, float unaccelerated_delta) { |
+ if (unaccelerated_delta == 0.f || accelerated_delta == 0.f) |
+ return 1.f; |
+ return unaccelerated_delta / accelerated_delta; |
+} |
+ |
+// Returns |kInvalidTouchIndex| iff |event| lacks a touch with an ID of |id|. |
+int GetIndexOfTouchID(const WebTouchEvent& event, int id) { |
+ for (unsigned i = 0; i < event.touchesLength; ++i) { |
+ if (event.touches[i].id == id) |
+ return i; |
+ } |
+ return kInvalidTouchIndex; |
+} |
+ |
+WebInputEvent::DispatchType MergeDispatchTypes( |
+ WebInputEvent::DispatchType type_1, |
+ WebInputEvent::DispatchType type_2) { |
+ static_assert(WebInputEvent::DispatchType::Blocking < |
+ WebInputEvent::DispatchType::EventNonBlocking, |
+ "Enum not ordered correctly"); |
+ static_assert(WebInputEvent::DispatchType::EventNonBlocking < |
+ WebInputEvent::DispatchType::ListenersNonBlockingPassive, |
+ "Enum not ordered correctly"); |
+ static_assert( |
+ WebInputEvent::DispatchType::ListenersNonBlockingPassive < |
+ WebInputEvent::DispatchType::ListenersForcedNonBlockingPassive, |
+ "Enum not ordered correctly"); |
+ return static_cast<WebInputEvent::DispatchType>( |
+ std::min(static_cast<int>(type_1), static_cast<int>(type_2))); |
+} |
+ |
+bool CanCoalesce(const blink::WebMouseEvent& event_to_coalesce, |
+ const blink::WebMouseEvent& event) { |
+ return event.type == event_to_coalesce.type && |
+ event.type == WebInputEvent::MouseMove; |
+} |
+ |
+void Coalesce(const WebMouseEvent& event_to_coalesce, WebMouseEvent* event) { |
+ DCHECK(CanCoalesce(event_to_coalesce, *event)); |
+ // Accumulate movement deltas. |
+ int x = event->movementX; |
+ int y = event->movementY; |
+ *event = event_to_coalesce; |
+ event->movementX += x; |
+ event->movementY += y; |
+} |
+ |
+bool CanCoalesce(const WebMouseWheelEvent& event_to_coalesce, |
+ const WebMouseWheelEvent& event) { |
+ return event.modifiers == event_to_coalesce.modifiers && |
+ event.scrollByPage == event_to_coalesce.scrollByPage && |
+ event.phase == event_to_coalesce.phase && |
+ event.momentumPhase == event_to_coalesce.momentumPhase && |
+ event.hasPreciseScrollingDeltas == |
+ event_to_coalesce.hasPreciseScrollingDeltas; |
+} |
+ |
+void Coalesce(const WebMouseWheelEvent& event_to_coalesce, |
+ WebMouseWheelEvent* event) { |
+ DCHECK(CanCoalesce(event_to_coalesce, *event)); |
+ float unaccelerated_x = |
+ GetUnacceleratedDelta(event->deltaX, event->accelerationRatioX) + |
+ GetUnacceleratedDelta(event_to_coalesce.deltaX, |
+ event_to_coalesce.accelerationRatioX); |
+ float unaccelerated_y = |
+ GetUnacceleratedDelta(event->deltaY, event->accelerationRatioY) + |
+ GetUnacceleratedDelta(event_to_coalesce.deltaY, |
+ event_to_coalesce.accelerationRatioY); |
+ event->deltaX += event_to_coalesce.deltaX; |
+ event->deltaY += event_to_coalesce.deltaY; |
+ event->wheelTicksX += event_to_coalesce.wheelTicksX; |
+ event->wheelTicksY += event_to_coalesce.wheelTicksY; |
+ event->accelerationRatioX = |
+ GetAccelerationRatio(event->deltaX, unaccelerated_x); |
+ event->accelerationRatioY = |
+ GetAccelerationRatio(event->deltaY, unaccelerated_y); |
+} |
+ |
+bool CanCoalesce(const WebTouchEvent& event_to_coalesce, |
+ const WebTouchEvent& event) { |
+ if (event.type != event_to_coalesce.type || |
+ event.type != WebInputEvent::TouchMove || |
+ event.modifiers != event_to_coalesce.modifiers || |
+ event.touchesLength != event_to_coalesce.touchesLength || |
+ event.touchesLength > WebTouchEvent::touchesLengthCap) |
+ return false; |
+ |
+ static_assert(WebTouchEvent::touchesLengthCap <= sizeof(int32_t) * 8U, |
+ "suboptimal touchesLengthCap size"); |
+ // Ensure that we have a 1-to-1 mapping of pointer ids between touches. |
+ std::bitset<WebTouchEvent::touchesLengthCap> unmatched_event_touches( |
+ (1 << event.touchesLength) - 1); |
+ for (unsigned i = 0; i < event_to_coalesce.touchesLength; ++i) { |
+ int event_touch_index = |
+ GetIndexOfTouchID(event, event_to_coalesce.touches[i].id); |
+ if (event_touch_index == kInvalidTouchIndex) |
+ return false; |
+ if (!unmatched_event_touches[event_touch_index]) |
+ return false; |
+ unmatched_event_touches[event_touch_index] = false; |
+ } |
+ return unmatched_event_touches.none(); |
+} |
+ |
+void Coalesce(const WebTouchEvent& event_to_coalesce, WebTouchEvent* event) { |
+ DCHECK(CanCoalesce(event_to_coalesce, *event)); |
+ // The WebTouchPoints include absolute position information. So it is |
+ // sufficient to simply replace the previous event with the new event-> |
+ // However, it is necessary to make sure that all the points have the |
+ // correct state, i.e. the touch-points that moved in the last event, but |
+ // didn't change in the current event, will have Stationary state. It is |
+ // necessary to change them back to Moved state. |
+ WebTouchEvent old_event = *event; |
+ *event = event_to_coalesce; |
+ for (unsigned i = 0; i < event->touchesLength; ++i) { |
+ int i_old = GetIndexOfTouchID(old_event, event->touches[i].id); |
+ if (old_event.touches[i_old].state == blink::WebTouchPoint::StateMoved) |
+ event->touches[i].state = blink::WebTouchPoint::StateMoved; |
+ } |
+ event->movedBeyondSlopRegion |= old_event.movedBeyondSlopRegion; |
+ event->dispatchType = MergeDispatchTypes(old_event.dispatchType, |
+ event_to_coalesce.dispatchType); |
+} |
+ |
+bool CanCoalesce(const WebGestureEvent& event_to_coalesce, |
+ const WebGestureEvent& event) { |
+ if (event.type != event_to_coalesce.type || |
+ event.sourceDevice != event_to_coalesce.sourceDevice || |
+ event.modifiers != event_to_coalesce.modifiers) |
+ return false; |
+ |
+ if (event.type == WebInputEvent::GestureScrollUpdate) |
+ return true; |
+ |
+ // GesturePinchUpdate scales can be combined only if they share a focal point, |
+ // e.g., with double-tap drag zoom. |
+ if (event.type == WebInputEvent::GesturePinchUpdate && |
+ event.x == event_to_coalesce.x && event.y == event_to_coalesce.y) |
+ return true; |
+ |
+ return false; |
+} |
+ |
+void Coalesce(const WebGestureEvent& event_to_coalesce, |
+ WebGestureEvent* event) { |
+ DCHECK(CanCoalesce(event_to_coalesce, *event)); |
+ if (event->type == WebInputEvent::GestureScrollUpdate) { |
+ event->data.scrollUpdate.deltaX += |
+ event_to_coalesce.data.scrollUpdate.deltaX; |
+ event->data.scrollUpdate.deltaY += |
+ event_to_coalesce.data.scrollUpdate.deltaY; |
+ DCHECK_EQ( |
+ event->data.scrollUpdate.previousUpdateInSequencePrevented, |
+ event_to_coalesce.data.scrollUpdate.previousUpdateInSequencePrevented); |
+ } else if (event->type == WebInputEvent::GesturePinchUpdate) { |
+ event->data.pinchUpdate.scale *= event_to_coalesce.data.pinchUpdate.scale; |
+ // Ensure the scale remains bounded above 0 and below Infinity so that |
+ // we can reliably perform operations like log on the values. |
+ if (event->data.pinchUpdate.scale < numeric_limits<float>::min()) |
+ event->data.pinchUpdate.scale = numeric_limits<float>::min(); |
+ else if (event->data.pinchUpdate.scale > numeric_limits<float>::max()) |
+ event->data.pinchUpdate.scale = numeric_limits<float>::max(); |
+ } |
+} |
+ |
+} // namespace |
+ |
+template <class T> |
+bool EventWithLatencyInfo<T>::CanCoalesceWith( |
+ const EventWithLatencyInfo& other) const { |
+ if (other.event.type != event.type) |
+ return false; |
+ |
+ DCHECK_EQ(sizeof(T), event.size); |
+ DCHECK_EQ(sizeof(T), other.event.size); |
+ |
+ return CanCoalesce(other.event, event); |
+} |
+ |
+template <class T> |
+void EventWithLatencyInfo<T>::CoalesceWith(const EventWithLatencyInfo& other) { |
+ // |other| should be a newer event than |this|. |
+ if (other.latency.trace_id() >= 0 && latency.trace_id() >= 0) |
+ DCHECK_GT(other.latency.trace_id(), latency.trace_id()); |
+ |
+ // New events get coalesced into older events, and the newer timestamp |
+ // should always be preserved. |
+ const double time_stamp_seconds = other.event.timeStampSeconds; |
+ Coalesce(other.event, &event); |
+ event.timeStampSeconds = time_stamp_seconds; |
+ |
+ // When coalescing two input events, we keep the oldest LatencyInfo |
+ // for Telemetry latency tests, since it will represent the longest |
+ // latency. |
+ other.latency = latency; |
+ other.latency.set_coalesced(); |
+} |
+ |
+// Explicit instantiations. |
+template class EXPORT_TEMPLATE_DEFINE(CONTENT_EXPORT) |
+ EventWithLatencyInfo<blink::WebGestureEvent>; |
+template class EXPORT_TEMPLATE_DEFINE(CONTENT_EXPORT) |
+ EventWithLatencyInfo<blink::WebMouseWheelEvent>; |
+template class EXPORT_TEMPLATE_DEFINE(CONTENT_EXPORT) |
+ EventWithLatencyInfo<blink::WebMouseEvent>; |
+template class EXPORT_TEMPLATE_DEFINE(CONTENT_EXPORT) |
+ EventWithLatencyInfo<blink::WebTouchEvent>; |
+ |
+} // namespace content |