| // Copyright 2020 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 "cc/metrics/event_metrics.h" |
| |
| #include <algorithm> |
| #include <ostream> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/notreached.h" |
| #include "base/time/default_tick_clock.h" |
| #include "cc/metrics/event_latency_tracing_recorder.h" |
| |
| namespace cc { |
| namespace { |
| |
| constexpr struct { |
| EventMetrics::EventType metrics_event_type; |
| ui::EventType ui_event_type; |
| const char* name; |
| absl::optional<bool> scroll_is_inertial = absl::nullopt; |
| absl::optional<ScrollUpdateEventMetrics::ScrollUpdateType> |
| scroll_update_type = absl::nullopt; |
| } kInterestingEvents[] = { |
| #define EVENT_TYPE(name, ui_type, ...) \ |
| { EventMetrics::EventType::k##name, ui_type, #name, __VA_ARGS__ } |
| EVENT_TYPE(MousePressed, ui::ET_MOUSE_PRESSED), |
| EVENT_TYPE(MouseReleased, ui::ET_MOUSE_RELEASED), |
| EVENT_TYPE(MouseWheel, ui::ET_MOUSEWHEEL), |
| EVENT_TYPE(KeyPressed, ui::ET_KEY_PRESSED), |
| EVENT_TYPE(KeyReleased, ui::ET_KEY_RELEASED), |
| EVENT_TYPE(TouchPressed, ui::ET_TOUCH_PRESSED), |
| EVENT_TYPE(TouchReleased, ui::ET_TOUCH_RELEASED), |
| EVENT_TYPE(TouchMoved, ui::ET_TOUCH_MOVED), |
| EVENT_TYPE(GestureScrollBegin, ui::ET_GESTURE_SCROLL_BEGIN, false), |
| EVENT_TYPE(GestureScrollUpdate, |
| ui::ET_GESTURE_SCROLL_UPDATE, |
| false, |
| ScrollUpdateEventMetrics::ScrollUpdateType::kContinued), |
| EVENT_TYPE(GestureScrollEnd, ui::ET_GESTURE_SCROLL_END, false), |
| EVENT_TYPE(GestureDoubleTap, ui::ET_GESTURE_DOUBLE_TAP), |
| EVENT_TYPE(GestureLongPress, ui::ET_GESTURE_LONG_PRESS), |
| EVENT_TYPE(GestureLongTap, ui::ET_GESTURE_LONG_TAP), |
| EVENT_TYPE(GestureShowPress, ui::ET_GESTURE_SHOW_PRESS), |
| EVENT_TYPE(GestureTap, ui::ET_GESTURE_TAP), |
| EVENT_TYPE(GestureTapCancel, ui::ET_GESTURE_TAP_CANCEL), |
| EVENT_TYPE(GestureTapDown, ui::ET_GESTURE_TAP_DOWN), |
| EVENT_TYPE(GestureTapUnconfirmed, ui::ET_GESTURE_TAP_UNCONFIRMED), |
| EVENT_TYPE(GestureTwoFingerTap, ui::ET_GESTURE_TWO_FINGER_TAP), |
| EVENT_TYPE(FirstGestureScrollUpdate, |
| ui::ET_GESTURE_SCROLL_UPDATE, |
| false, |
| ScrollUpdateEventMetrics::ScrollUpdateType::kStarted), |
| EVENT_TYPE(MouseDragged, ui::ET_MOUSE_DRAGGED), |
| EVENT_TYPE(GesturePinchBegin, ui::ET_GESTURE_PINCH_BEGIN), |
| EVENT_TYPE(GesturePinchEnd, ui::ET_GESTURE_PINCH_END), |
| EVENT_TYPE(GesturePinchUpdate, ui::ET_GESTURE_PINCH_UPDATE), |
| EVENT_TYPE(InertialGestureScrollUpdate, |
| ui::ET_GESTURE_SCROLL_UPDATE, |
| true, |
| ScrollUpdateEventMetrics::ScrollUpdateType::kContinued), |
| #undef EVENT_TYPE |
| }; |
| static_assert(std::size(kInterestingEvents) == |
| static_cast<int>(EventMetrics::EventType::kMaxValue) + 1, |
| "EventMetrics::EventType has changed."); |
| |
| constexpr struct { |
| ScrollEventMetrics::ScrollType metrics_scroll_type; |
| ui::ScrollInputType ui_input_type; |
| const char* name; |
| } kScrollTypes[] = { |
| #define SCROLL_TYPE(name) \ |
| { \ |
| ScrollEventMetrics::ScrollType::k##name, ui::ScrollInputType::k##name, \ |
| #name \ |
| } |
| SCROLL_TYPE(Autoscroll), |
| SCROLL_TYPE(Scrollbar), |
| SCROLL_TYPE(Touchscreen), |
| SCROLL_TYPE(Wheel), |
| #undef SCROLL_TYPE |
| }; |
| static_assert(std::size(kScrollTypes) == |
| static_cast<int>(ScrollEventMetrics::ScrollType::kMaxValue) + |
| 1, |
| "ScrollEventMetrics::ScrollType has changed."); |
| |
| constexpr struct { |
| PinchEventMetrics::PinchType metrics_pinch_type; |
| ui::ScrollInputType ui_input_type; |
| const char* name; |
| } kPinchTypes[] = { |
| #define PINCH_TYPE(metrics_name, ui_name) \ |
| { \ |
| PinchEventMetrics::PinchType::k##metrics_name, \ |
| ui::ScrollInputType::k##ui_name, #metrics_name \ |
| } |
| PINCH_TYPE(Touchpad, Wheel), |
| PINCH_TYPE(Touchscreen, Touchscreen), |
| #undef PINCH_TYPE |
| }; |
| static_assert(std::size(kPinchTypes) == |
| static_cast<int>(PinchEventMetrics::PinchType::kMaxValue) + 1, |
| "PinchEventMetrics::PinchType has changed."); |
| |
| absl::optional<EventMetrics::EventType> ToInterestingEventType( |
| ui::EventType ui_event_type, |
| absl::optional<bool> scroll_is_inertial, |
| absl::optional<ScrollUpdateEventMetrics::ScrollUpdateType> |
| scroll_update_type) { |
| for (size_t i = 0; i < std::size(kInterestingEvents); i++) { |
| const auto& interesting_event = kInterestingEvents[i]; |
| if (ui_event_type == interesting_event.ui_event_type && |
| scroll_is_inertial == interesting_event.scroll_is_inertial && |
| scroll_update_type == interesting_event.scroll_update_type) { |
| EventMetrics::EventType metrics_event_type = |
| static_cast<EventMetrics::EventType>(i); |
| DCHECK_EQ(metrics_event_type, interesting_event.metrics_event_type); |
| return metrics_event_type; |
| } |
| } |
| return absl::nullopt; |
| } |
| |
| ScrollEventMetrics::ScrollType ToScrollType(ui::ScrollInputType ui_input_type) { |
| for (size_t i = 0; i < std::size(kScrollTypes); i++) { |
| if (ui_input_type == kScrollTypes[i].ui_input_type) { |
| auto metrics_scroll_type = static_cast<ScrollEventMetrics::ScrollType>(i); |
| DCHECK_EQ(metrics_scroll_type, kScrollTypes[i].metrics_scroll_type); |
| return metrics_scroll_type; |
| } |
| } |
| NOTREACHED(); |
| return ScrollEventMetrics::ScrollType::kMaxValue; |
| } |
| |
| PinchEventMetrics::PinchType ToPinchType(ui::ScrollInputType ui_input_type) { |
| for (size_t i = 0; i < std::size(kPinchTypes); i++) { |
| if (ui_input_type == kPinchTypes[i].ui_input_type) { |
| auto metrics_pinch_type = static_cast<PinchEventMetrics::PinchType>(i); |
| DCHECK_EQ(metrics_pinch_type, kPinchTypes[i].metrics_pinch_type); |
| return metrics_pinch_type; |
| } |
| } |
| NOTREACHED(); |
| return PinchEventMetrics::PinchType::kMaxValue; |
| } |
| |
| bool IsGestureScroll(ui::EventType type) { |
| return type == ui::ET_GESTURE_SCROLL_BEGIN || |
| type == ui::ET_GESTURE_SCROLL_UPDATE || |
| type == ui::ET_GESTURE_SCROLL_END; |
| } |
| |
| bool IsGesturePinch(ui::EventType type) { |
| return type == ui::ET_GESTURE_PINCH_BEGIN || |
| type == ui::ET_GESTURE_PINCH_UPDATE || |
| type == ui::ET_GESTURE_PINCH_END; |
| } |
| |
| bool IsGestureScrollUpdate(ui::EventType type) { |
| return type == ui::ET_GESTURE_SCROLL_UPDATE; |
| } |
| |
| } // namespace |
| |
| // EventMetrics: |
| |
| // static |
| std::unique_ptr<EventMetrics> EventMetrics::Create(ui::EventType type, |
| base::TimeTicks timestamp) { |
| // TODO(crbug.com/1157090): We expect that `timestamp` is not null, but there |
| // seems to be some tests that are emitting events with null timestamp. We |
| // should investigate and try to fix those cases and add a `DCHECK` here to |
| // assert `timestamp` is not null. |
| |
| DCHECK(!IsGestureScroll(type) && !IsGesturePinch(type)); |
| |
| std::unique_ptr<EventMetrics> metrics = |
| CreateInternal(type, timestamp, base::DefaultTickClock::GetInstance()); |
| if (!metrics) |
| return nullptr; |
| |
| metrics->SetDispatchStageTimestamp( |
| DispatchStage::kArrivedInRendererCompositor); |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<EventMetrics> EventMetrics::CreateForTesting( |
| ui::EventType type, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) { |
| DCHECK(!timestamp.is_null()); |
| |
| std::unique_ptr<EventMetrics> metrics = |
| CreateInternal(type, timestamp, tick_clock); |
| if (!metrics) |
| return nullptr; |
| |
| metrics->SetDispatchStageTimestamp( |
| DispatchStage::kArrivedInRendererCompositor); |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<EventMetrics> EventMetrics::CreateFromExisting( |
| ui::EventType type, |
| DispatchStage last_dispatch_stage, |
| const EventMetrics* existing) { |
| // Generally, if `existing` is `nullptr` (the existing event is not of an |
| // interesting type), the new event won't be of an interesting type, too, and |
| // we can immediately return `nullptr`. The only exception is some tests that |
| // are not interested in reporting metrics, in which case we can immediately |
| // return `nullptr`, too, as they are not interested in reporting metrics. |
| if (!existing) |
| return nullptr; |
| |
| std::unique_ptr<EventMetrics> metrics = |
| CreateInternal(type, base::TimeTicks(), existing->tick_clock_); |
| if (!metrics) |
| return nullptr; |
| |
| // Use timestamps of all stages (including "Generated" stage) up to |
| // `last_dispatch_stage` from `existing`. |
| metrics->CopyTimestampsFrom(*existing, last_dispatch_stage); |
| |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<EventMetrics> EventMetrics::CreateInternal( |
| ui::EventType type, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) { |
| absl::optional<EventType> interesting_type = |
| ToInterestingEventType(type, /*scroll_is_inertial=*/absl::nullopt, |
| /*scroll_update_type=*/absl::nullopt); |
| if (!interesting_type) |
| return nullptr; |
| return base::WrapUnique( |
| new EventMetrics(*interesting_type, timestamp, tick_clock)); |
| } |
| |
| EventMetrics::EventMetrics(EventType type, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) |
| : type_(type), tick_clock_(tick_clock) { |
| dispatch_stage_timestamps_[static_cast<int>(DispatchStage::kGenerated)] = |
| timestamp; |
| } |
| |
| EventMetrics::EventMetrics(const EventMetrics& other) |
| : type_(other.type_), tick_clock_(other.tick_clock_) { |
| CopyTimestampsFrom(other, DispatchStage::kMaxValue); |
| } |
| |
| EventMetrics::~EventMetrics() { |
| if (!is_tracing_recorded()) { |
| EventLatencyTracingRecorder::RecordEventLatencyTraceEvent( |
| this, base::TimeTicks::Now(), nullptr, nullptr); |
| } |
| } |
| |
| const char* EventMetrics::GetTypeName() const { |
| return kInterestingEvents[static_cast<int>(type_)].name; |
| } |
| |
| void EventMetrics::SetDispatchStageTimestamp(DispatchStage stage) { |
| DCHECK(dispatch_stage_timestamps_[static_cast<size_t>(stage)].is_null()); |
| |
| dispatch_stage_timestamps_[static_cast<size_t>(stage)] = |
| tick_clock_->NowTicks(); |
| } |
| |
| base::TimeTicks EventMetrics::GetDispatchStageTimestamp( |
| DispatchStage stage) const { |
| return dispatch_stage_timestamps_[static_cast<size_t>(stage)]; |
| } |
| |
| void EventMetrics::ResetToDispatchStage(DispatchStage stage) { |
| for (size_t stage_index = static_cast<size_t>(stage) + 1; |
| stage_index <= static_cast<size_t>(DispatchStage::kMaxValue); |
| stage_index++) { |
| dispatch_stage_timestamps_[stage_index] = base::TimeTicks(); |
| } |
| } |
| |
| bool EventMetrics::HasSmoothInputEvent() const { |
| return type_ == EventType::kMouseDragged || type_ == EventType::kTouchMoved; |
| } |
| |
| ScrollEventMetrics* EventMetrics::AsScroll() { |
| return nullptr; |
| } |
| |
| const ScrollEventMetrics* EventMetrics::AsScroll() const { |
| return const_cast<EventMetrics*>(this)->AsScroll(); |
| } |
| |
| ScrollUpdateEventMetrics* EventMetrics::AsScrollUpdate() { |
| return nullptr; |
| } |
| |
| const ScrollUpdateEventMetrics* EventMetrics::AsScrollUpdate() const { |
| return const_cast<EventMetrics*>(this)->AsScrollUpdate(); |
| } |
| |
| PinchEventMetrics* EventMetrics::AsPinch() { |
| return nullptr; |
| } |
| |
| const PinchEventMetrics* EventMetrics::AsPinch() const { |
| return const_cast<EventMetrics*>(this)->AsPinch(); |
| } |
| |
| std::unique_ptr<EventMetrics> EventMetrics::Clone() const { |
| return base::WrapUnique(new EventMetrics(*this)); |
| } |
| |
| void EventMetrics::CopyTimestampsFrom(const EventMetrics& other, |
| DispatchStage last_dispatch_stage) { |
| DCHECK_LE(last_dispatch_stage, DispatchStage::kMaxValue); |
| std::copy(other.dispatch_stage_timestamps_, |
| other.dispatch_stage_timestamps_ + |
| static_cast<size_t>(last_dispatch_stage) + 1, |
| dispatch_stage_timestamps_); |
| } |
| |
| // ScrollEventMetrics |
| |
| // static |
| std::unique_ptr<ScrollEventMetrics> ScrollEventMetrics::Create( |
| ui::EventType type, |
| ui::ScrollInputType input_type, |
| bool is_inertial, |
| base::TimeTicks timestamp) { |
| // TODO(crbug.com/1157090): We expect that `timestamp` is not null, but there |
| // seems to be some tests that are emitting events with null timestamp. We |
| // should investigate and try to fix those cases and add a `DCHECK` here to |
| // assert `timestamp` is not null. |
| |
| DCHECK(IsGestureScroll(type) && !IsGestureScrollUpdate(type)); |
| |
| std::unique_ptr<ScrollEventMetrics> metrics = |
| CreateInternal(type, input_type, is_inertial, timestamp, |
| base::DefaultTickClock::GetInstance()); |
| if (!metrics) |
| return nullptr; |
| |
| metrics->SetDispatchStageTimestamp( |
| DispatchStage::kArrivedInRendererCompositor); |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<ScrollEventMetrics> ScrollEventMetrics::CreateForTesting( |
| ui::EventType type, |
| ui::ScrollInputType input_type, |
| bool is_inertial, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) { |
| DCHECK(!timestamp.is_null()); |
| |
| std::unique_ptr<ScrollEventMetrics> metrics = |
| CreateInternal(type, input_type, is_inertial, timestamp, tick_clock); |
| if (!metrics) |
| return nullptr; |
| |
| metrics->SetDispatchStageTimestamp( |
| DispatchStage::kArrivedInRendererCompositor); |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<ScrollEventMetrics> ScrollEventMetrics::CreateFromExisting( |
| ui::EventType type, |
| ui::ScrollInputType input_type, |
| bool is_inertial, |
| DispatchStage last_dispatch_stage, |
| const EventMetrics* existing) { |
| // Generally, if `existing` is `nullptr` (the existing event is not of an |
| // interesting type), the new event won't be of an interesting type, too, and |
| // we can immediately return `nullptr`. The only exception is some tests that |
| // are not interested in reporting metrics, in which case we can immediately |
| // return `nullptr`, too, as they are not interested in reporting metrics. |
| if (!existing) |
| return nullptr; |
| |
| std::unique_ptr<ScrollEventMetrics> metrics = CreateInternal( |
| type, input_type, is_inertial, base::TimeTicks(), existing->tick_clock_); |
| if (!metrics) |
| return nullptr; |
| |
| // Use timestamps of all stages (including "Generated" stage) up to |
| // `last_dispatch_stage` from `existing`. |
| metrics->CopyTimestampsFrom(*existing, last_dispatch_stage); |
| |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<ScrollEventMetrics> ScrollEventMetrics::CreateInternal( |
| ui::EventType type, |
| ui::ScrollInputType input_type, |
| bool is_inertial, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) { |
| absl::optional<EventType> interesting_type = |
| ToInterestingEventType(type, is_inertial, |
| /*scroll_update_type=*/absl::nullopt); |
| if (!interesting_type) |
| return nullptr; |
| return base::WrapUnique(new ScrollEventMetrics( |
| *interesting_type, ToScrollType(input_type), timestamp, tick_clock)); |
| } |
| |
| ScrollEventMetrics::ScrollEventMetrics(EventType type, |
| ScrollType scroll_type, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) |
| : EventMetrics(type, timestamp, tick_clock), scroll_type_(scroll_type) {} |
| |
| ScrollEventMetrics::ScrollEventMetrics(const ScrollEventMetrics&) = default; |
| |
| ScrollEventMetrics::~ScrollEventMetrics() { |
| if (!is_tracing_recorded()) { |
| EventLatencyTracingRecorder::RecordEventLatencyTraceEvent( |
| this, base::TimeTicks::Now(), nullptr, nullptr); |
| } |
| } |
| |
| const char* ScrollEventMetrics::GetScrollTypeName() const { |
| return kScrollTypes[static_cast<int>(scroll_type_)].name; |
| } |
| |
| ScrollEventMetrics* ScrollEventMetrics::AsScroll() { |
| return this; |
| } |
| |
| std::unique_ptr<EventMetrics> ScrollEventMetrics::Clone() const { |
| return base::WrapUnique(new ScrollEventMetrics(*this)); |
| } |
| |
| // ScrollUpdateEventMetrics |
| |
| // static |
| std::unique_ptr<ScrollUpdateEventMetrics> ScrollUpdateEventMetrics::Create( |
| ui::EventType type, |
| ui::ScrollInputType input_type, |
| bool is_inertial, |
| ScrollUpdateType scroll_update_type, |
| float delta, |
| base::TimeTicks timestamp) { |
| // TODO(crbug.com/1157090): We expect that `timestamp` is not null, but there |
| // seems to be some tests that are emitting events with null timestamp. We |
| // should investigate and try to fix those cases and add a `DCHECK` here to |
| // assert `timestamp` is not null. |
| |
| DCHECK(IsGestureScrollUpdate(type)); |
| |
| std::unique_ptr<ScrollUpdateEventMetrics> metrics = |
| CreateInternal(type, input_type, is_inertial, scroll_update_type, delta, |
| timestamp, base::DefaultTickClock::GetInstance()); |
| if (!metrics) |
| return nullptr; |
| |
| metrics->SetDispatchStageTimestamp( |
| DispatchStage::kArrivedInRendererCompositor); |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<ScrollUpdateEventMetrics> |
| ScrollUpdateEventMetrics::CreateForTesting(ui::EventType type, |
| ui::ScrollInputType input_type, |
| bool is_inertial, |
| ScrollUpdateType scroll_update_type, |
| float delta, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) { |
| DCHECK(!timestamp.is_null()); |
| |
| std::unique_ptr<ScrollUpdateEventMetrics> metrics = |
| CreateInternal(type, input_type, is_inertial, scroll_update_type, delta, |
| timestamp, tick_clock); |
| if (!metrics) |
| return nullptr; |
| |
| metrics->SetDispatchStageTimestamp( |
| DispatchStage::kArrivedInRendererCompositor); |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<ScrollUpdateEventMetrics> |
| ScrollUpdateEventMetrics::CreateFromExisting( |
| ui::EventType type, |
| ui::ScrollInputType input_type, |
| bool is_inertial, |
| ScrollUpdateType scroll_update_type, |
| float delta, |
| DispatchStage last_dispatch_stage, |
| const EventMetrics* existing) { |
| // Since the new event is of an interesting type, we expect the existing event |
| // to be of an interesting type, too; which means `existing` should not be |
| // `nullptr`. However, some tests that are not interested in reporting |
| // metrics, don't create metrics objects even for events of interesting types. |
| // Return `nullptr` if that's the case. |
| if (!existing) |
| return nullptr; |
| |
| std::unique_ptr<ScrollUpdateEventMetrics> metrics = |
| CreateInternal(type, input_type, is_inertial, scroll_update_type, delta, |
| base::TimeTicks(), existing->tick_clock_); |
| if (!metrics) |
| return nullptr; |
| |
| // Use timestamps of all stages (including "Generated" stage) up to |
| // `last_dispatch_stage` from `existing`. |
| metrics->CopyTimestampsFrom(*existing, last_dispatch_stage); |
| |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<ScrollUpdateEventMetrics> |
| ScrollUpdateEventMetrics::CreateInternal(ui::EventType type, |
| ui::ScrollInputType input_type, |
| bool is_inertial, |
| ScrollUpdateType scroll_update_type, |
| float delta, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) { |
| absl::optional<EventType> interesting_type = |
| ToInterestingEventType(type, is_inertial, scroll_update_type); |
| if (!interesting_type) |
| return nullptr; |
| return base::WrapUnique(new ScrollUpdateEventMetrics( |
| *interesting_type, ToScrollType(input_type), scroll_update_type, delta, |
| timestamp, tick_clock)); |
| } |
| |
| ScrollUpdateEventMetrics::ScrollUpdateEventMetrics( |
| EventType type, |
| ScrollType scroll_type, |
| ScrollUpdateType scroll_update_type, |
| float delta, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) |
| : ScrollEventMetrics(type, scroll_type, timestamp, tick_clock), |
| delta_(delta), |
| predicted_delta_(delta), |
| last_timestamp_(timestamp) {} |
| |
| ScrollUpdateEventMetrics::ScrollUpdateEventMetrics( |
| const ScrollUpdateEventMetrics&) = default; |
| |
| ScrollUpdateEventMetrics::~ScrollUpdateEventMetrics() { |
| if (!is_tracing_recorded()) { |
| EventLatencyTracingRecorder::RecordEventLatencyTraceEvent( |
| this, base::TimeTicks::Now(), nullptr, nullptr); |
| } |
| } |
| |
| void ScrollUpdateEventMetrics::CoalesceWith( |
| const ScrollUpdateEventMetrics& newer_scroll_update) { |
| last_timestamp_ = newer_scroll_update.last_timestamp_; |
| delta_ += newer_scroll_update.delta_; |
| predicted_delta_ += newer_scroll_update.predicted_delta_; |
| } |
| |
| ScrollUpdateEventMetrics* ScrollUpdateEventMetrics::AsScrollUpdate() { |
| return this; |
| } |
| |
| std::unique_ptr<EventMetrics> ScrollUpdateEventMetrics::Clone() const { |
| return base::WrapUnique(new ScrollUpdateEventMetrics(*this)); |
| } |
| |
| // PinchEventMetrics |
| |
| // static |
| std::unique_ptr<PinchEventMetrics> PinchEventMetrics::Create( |
| ui::EventType type, |
| ui::ScrollInputType input_type, |
| base::TimeTicks timestamp) { |
| // TODO(crbug.com/1157090): We expect that `timestamp` is not null, but there |
| // seems to be some tests that are emitting events with null timestamp. We |
| // should investigate and try to fix those cases and add a `DCHECK` here to |
| // assert `timestamp` is not null. |
| |
| DCHECK(IsGesturePinch(type)); |
| |
| std::unique_ptr<PinchEventMetrics> metrics = CreateInternal( |
| type, input_type, timestamp, base::DefaultTickClock::GetInstance()); |
| if (!metrics) |
| return nullptr; |
| |
| metrics->SetDispatchStageTimestamp( |
| DispatchStage::kArrivedInRendererCompositor); |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<PinchEventMetrics> PinchEventMetrics::CreateForTesting( |
| ui::EventType type, |
| ui::ScrollInputType input_type, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) { |
| DCHECK(!timestamp.is_null()); |
| |
| std::unique_ptr<PinchEventMetrics> metrics = |
| CreateInternal(type, input_type, timestamp, tick_clock); |
| if (!metrics) |
| return nullptr; |
| |
| metrics->SetDispatchStageTimestamp( |
| DispatchStage::kArrivedInRendererCompositor); |
| return metrics; |
| } |
| |
| // static |
| std::unique_ptr<PinchEventMetrics> PinchEventMetrics::CreateInternal( |
| ui::EventType type, |
| ui::ScrollInputType input_type, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) { |
| absl::optional<EventType> interesting_type = |
| ToInterestingEventType(type, /*scroll_is_inertial=*/absl::nullopt, |
| /*scroll_update_type=*/absl::nullopt); |
| if (!interesting_type) |
| return nullptr; |
| return base::WrapUnique(new PinchEventMetrics( |
| *interesting_type, ToPinchType(input_type), timestamp, tick_clock)); |
| } |
| |
| PinchEventMetrics::PinchEventMetrics(EventType type, |
| PinchType pinch_type, |
| base::TimeTicks timestamp, |
| const base::TickClock* tick_clock) |
| : EventMetrics(type, timestamp, tick_clock), pinch_type_(pinch_type) {} |
| |
| PinchEventMetrics::PinchEventMetrics(const PinchEventMetrics&) = default; |
| |
| PinchEventMetrics::~PinchEventMetrics() { |
| if (!is_tracing_recorded()) { |
| EventLatencyTracingRecorder::RecordEventLatencyTraceEvent( |
| this, base::TimeTicks::Now(), nullptr, nullptr); |
| } |
| } |
| |
| const char* PinchEventMetrics::GetPinchTypeName() const { |
| return kPinchTypes[static_cast<int>(pinch_type_)].name; |
| } |
| |
| PinchEventMetrics* PinchEventMetrics::AsPinch() { |
| return this; |
| } |
| |
| std::unique_ptr<EventMetrics> PinchEventMetrics::Clone() const { |
| return base::WrapUnique(new PinchEventMetrics(*this)); |
| } |
| |
| // EventMetricsSet |
| EventMetricsSet::EventMetricsSet() = default; |
| EventMetricsSet::~EventMetricsSet() = default; |
| EventMetricsSet::EventMetricsSet(EventMetrics::List main_thread_event_metrics, |
| EventMetrics::List impl_thread_event_metrics) |
| : main_event_metrics(std::move(main_thread_event_metrics)), |
| impl_event_metrics(std::move(impl_thread_event_metrics)) {} |
| EventMetricsSet::EventMetricsSet(EventMetricsSet&& other) = default; |
| EventMetricsSet& EventMetricsSet::operator=(EventMetricsSet&& other) = default; |
| |
| } // namespace cc |