| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/input/render_input_router_latency_tracker.h" |
| |
| #include <stddef.h> |
| |
| #include <string> |
| |
| #include "base/check_op.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/notreached.h" |
| #include "base/time/time.h" |
| #include "base/trace_event/trace_id_helper.h" |
| #include "build/build_config.h" |
| #include "components/input/render_input_router_delegate.h" |
| #include "ui/events/blink/web_input_event_traits.h" |
| |
| using blink::WebGestureEvent; |
| using blink::WebInputEvent; |
| using blink::WebMouseEvent; |
| using blink::WebMouseWheelEvent; |
| using blink::WebTouchEvent; |
| using ui::LatencyInfo; |
| |
| namespace input { |
| namespace { |
| const char* GetTraceNameFromType(blink::WebInputEvent::Type type) { |
| #define CASE_TYPE(t) \ |
| case WebInputEvent::Type::k##t: \ |
| return "InputLatency::" #t |
| switch (type) { |
| CASE_TYPE(Undefined); |
| CASE_TYPE(MouseDown); |
| CASE_TYPE(MouseUp); |
| CASE_TYPE(MouseMove); |
| CASE_TYPE(MouseEnter); |
| CASE_TYPE(MouseLeave); |
| CASE_TYPE(ContextMenu); |
| CASE_TYPE(MouseWheel); |
| CASE_TYPE(RawKeyDown); |
| CASE_TYPE(KeyDown); |
| CASE_TYPE(KeyUp); |
| CASE_TYPE(Char); |
| CASE_TYPE(GestureScrollBegin); |
| CASE_TYPE(GestureScrollEnd); |
| CASE_TYPE(GestureScrollUpdate); |
| CASE_TYPE(GestureFlingStart); |
| CASE_TYPE(GestureFlingCancel); |
| CASE_TYPE(GestureShowPress); |
| CASE_TYPE(GestureTap); |
| CASE_TYPE(GestureTapUnconfirmed); |
| CASE_TYPE(GestureTapDown); |
| CASE_TYPE(GestureTapCancel); |
| CASE_TYPE(GestureDoubleTap); |
| CASE_TYPE(GestureTwoFingerTap); |
| CASE_TYPE(GestureShortPress); |
| CASE_TYPE(GestureLongPress); |
| CASE_TYPE(GestureLongTap); |
| CASE_TYPE(GestureBegin); |
| CASE_TYPE(GestureEnd); |
| CASE_TYPE(GesturePinchBegin); |
| CASE_TYPE(GesturePinchEnd); |
| CASE_TYPE(GesturePinchUpdate); |
| CASE_TYPE(TouchStart); |
| CASE_TYPE(TouchMove); |
| CASE_TYPE(TouchEnd); |
| CASE_TYPE(TouchCancel); |
| CASE_TYPE(TouchScrollStarted); |
| CASE_TYPE(PointerDown); |
| CASE_TYPE(PointerUp); |
| CASE_TYPE(PointerMove); |
| CASE_TYPE(PointerRawUpdate); |
| CASE_TYPE(PointerCancel); |
| CASE_TYPE(PointerCausedUaAction); |
| } |
| #undef CASE_TYPE |
| NOTREACHED(); |
| } |
| } // namespace |
| |
| RenderInputRouterLatencyTracker::RenderInputRouterLatencyTracker( |
| RenderInputRouterDelegate* delegate) |
| : has_seen_first_gesture_scroll_update_(false), |
| gesture_scroll_id_(-1), |
| touch_trace_id_(-1), |
| active_multi_finger_gesture_(false), |
| touch_start_default_prevented_(false), |
| render_input_router_delegate_(delegate) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| RenderInputRouterLatencyTracker::~RenderInputRouterLatencyTracker() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| void RenderInputRouterLatencyTracker::OnInputEvent( |
| const blink::WebInputEvent& event, |
| LatencyInfo* latency, |
| ui::EventLatencyMetadata* event_latency_metadata) { |
| DCHECK(latency); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| OnEventStart(latency); |
| |
| if (event.GetType() == WebInputEvent::Type::kTouchStart) { |
| const WebTouchEvent& touch_event = |
| *static_cast<const WebTouchEvent*>(&event); |
| DCHECK_GE(touch_event.touches_length, static_cast<unsigned>(1)); |
| active_multi_finger_gesture_ = touch_event.touches_length != 1; |
| } |
| |
| // This is the only place to add the BEGIN_RWH component. So this component |
| // should not already be present in the latency info. |
| bool found_component = latency->FindLatency( |
| ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr); |
| DCHECK(!found_component); |
| |
| if (!event.TimeStamp().is_null() && |
| !latency->FindLatency(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, |
| nullptr)) { |
| base::TimeTicks timestamp_now = base::TimeTicks::Now(); |
| base::TimeTicks timestamp_original = event.TimeStamp(); |
| |
| // Timestamp from platform input can wrap, e.g. 32 bits timestamp |
| // for Xserver and Window MSG time will wrap about 49.6 days. Do a |
| // sanity check here and if wrap does happen, use TimeTicks::Now() |
| // as the timestamp instead. |
| if ((timestamp_now - timestamp_original).InDays() > 0) |
| timestamp_original = timestamp_now; |
| |
| latency->AddLatencyNumberWithTimestamp( |
| ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, timestamp_original); |
| } |
| |
| base::TimeTicks begin_rwh_timestamp = base::TimeTicks::Now(); |
| latency->AddLatencyNumberWithTraceName( |
| ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, |
| GetTraceNameFromType(event.GetType()), begin_rwh_timestamp); |
| event_latency_metadata->arrived_in_browser_main_timestamp = |
| begin_rwh_timestamp; |
| |
| if (event.GetType() == blink::WebInputEvent::Type::kGestureScrollBegin) { |
| has_seen_first_gesture_scroll_update_ = false; |
| gesture_scroll_id_ = latency->trace_id(); |
| latency->set_gesture_scroll_id(gesture_scroll_id_); |
| } else if (event.GetType() == |
| blink::WebInputEvent::Type::kGestureScrollUpdate) { |
| // Make a copy of the INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT with a |
| // different name INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT. |
| // So we can track the latency specifically for scroll update events. |
| base::TimeTicks original_event_timestamp; |
| if (latency->FindLatency(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, |
| &original_event_timestamp)) { |
| latency->AddLatencyNumberWithTimestamp( |
| has_seen_first_gesture_scroll_update_ |
| ? ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT |
| : ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT, |
| original_event_timestamp); |
| } |
| |
| has_seen_first_gesture_scroll_update_ = true; |
| latency->set_gesture_scroll_id(gesture_scroll_id_); |
| } else if (event.GetType() == blink::WebInputEvent::Type::kGestureScrollEnd) { |
| latency->set_gesture_scroll_id(gesture_scroll_id_); |
| gesture_scroll_id_ = -1; |
| } else if (blink::WebInputEvent::IsTouchEventType(event.GetType())) { |
| // Store the trace id for the TouchStart event on all other Touch events |
| // until the corresponding end or cancel event so they can be grouped |
| // together. |
| if (event.GetType() == blink::WebInputEvent::Type::kTouchStart) |
| touch_trace_id_ = latency->trace_id(); |
| latency->set_touch_trace_id(touch_trace_id_); |
| if (event.GetType() == blink::WebInputEvent::Type::kTouchEnd || |
| event.GetType() == blink::WebInputEvent::Type::kTouchCancel) |
| touch_trace_id_ = -1; |
| } |
| } |
| |
| void RenderInputRouterLatencyTracker::OnInputEventAck( |
| const blink::WebInputEvent& event, |
| LatencyInfo* latency, |
| blink::mojom::InputEventResultState ack_result) { |
| DCHECK(latency); |
| |
| // Latency ends if an event is acked but does not cause render scheduling. |
| bool rendering_scheduled = latency->FindLatency( |
| ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT, nullptr); |
| rendering_scheduled |= latency->FindLatency( |
| ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT, nullptr); |
| |
| if (WebInputEvent::IsTouchEventType(event.GetType())) { |
| const WebTouchEvent& touch_event = |
| *static_cast<const WebTouchEvent*>(&event); |
| if (event.GetType() == WebInputEvent::Type::kTouchStart) { |
| touch_start_default_prevented_ = |
| ack_result == blink::mojom::InputEventResultState::kConsumed; |
| } else if (event.GetType() == WebInputEvent::Type::kTouchEnd || |
| event.GetType() == WebInputEvent::Type::kTouchCancel) { |
| active_multi_finger_gesture_ = touch_event.touches_length > 2; |
| } |
| } |
| |
| // If this event couldn't have caused a gesture event, and it didn't trigger |
| // rendering, we're done processing it. If the event got coalesced then |
| // terminate it as well. We also exclude cases where we're against the scroll |
| // extent from scrolling metrics. |
| if (!rendering_scheduled || latency->coalesced() || |
| (event.GetType() == WebInputEvent::Type::kGestureScrollUpdate && |
| ack_result == blink::mojom::InputEventResultState::kNoConsumerExists)) { |
| latency->Terminate(); |
| } |
| } |
| |
| void RenderInputRouterLatencyTracker::OnEventStart(ui::LatencyInfo* latency) { |
| if (latency->trace_id() == -1) { |
| latency->set_trace_id(base::trace_event::GetNextGlobalTraceId()); |
| } |
| } |
| |
| } // namespace input |