[go: nahoru, domu]

Add event latency to compositor latency metrics

If handling of an event leads to producing a compositor frame, an UMA
histogram is recorded measuring the time between the event timestamp and
presentation of the corresponding frame to the screen, in the form of
"EventLatency.<EventType>.TotalLatency". In future, we will add
appropriate break-down metrics, too.

Bug: 1054007

Change-Id: If85f99fb83172337c21b374073a3c2d098c2fd50
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2018704
Reviewed-by: Robert Kaplow <rkaplow@chromium.org>
Reviewed-by: Sadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: Navid Zolghadr <nzolghadr@chromium.org>
Reviewed-by: Behdad Bakhshinategh <behdadb@chromium.org>
Commit-Queue: Mohsen Izadi <mohsen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746247}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 4f65dd8..146f433 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -156,6 +156,10 @@
     "metrics/compositor_frame_reporting_controller.h",
     "metrics/compositor_timing_history.cc",
     "metrics/compositor_timing_history.h",
+    "metrics/event_metrics.cc",
+    "metrics/event_metrics.h",
+    "metrics/events_metrics_manager.cc",
+    "metrics/events_metrics_manager.h",
     "metrics/frame_sequence_tracker.cc",
     "metrics/frame_sequence_tracker.h",
     "metrics/latency_ukm_reporter.cc",
@@ -647,6 +651,7 @@
     "metrics/compositor_frame_reporter_unittest.cc",
     "metrics/compositor_frame_reporting_controller_unittest.cc",
     "metrics/compositor_timing_history_unittest.cc",
+    "metrics/events_metrics_manager_unittest.cc",
     "metrics/frame_sequence_metrics_unittest.cc",
     "metrics/frame_sequence_tracker_unittest.cc",
     "mojo_embedder/async_layer_tree_frame_sink_unittest.cc",
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 048e434..46c3b656 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -15,6 +15,8 @@
 #include "cc/input/scroll_state.h"
 #include "cc/input/scrollbar.h"
 #include "cc/input/touch_action.h"
+#include "cc/metrics/event_metrics.h"
+#include "cc/metrics/events_metrics_manager.h"
 #include "cc/paint/element_id.h"
 #include "cc/trees/swap_promise_monitor.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
@@ -255,6 +257,13 @@
   virtual std::unique_ptr<SwapPromiseMonitor>
   CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency) = 0;
 
+  // During the lifetime of the returned EventsMetricsManager::ScopedMonitor, if
+  // SetNeedsOneBeginImplFrame() or SetNeedsRedraw() are called on
+  // LayerTreeHostImpl or a scroll animation is updated, |event_metrics| will be
+  // saved for reporting event latency metrics.
+  virtual std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+  GetScopedEventMetricsMonitor(const EventMetrics& event_metrics) = 0;
+
   virtual ScrollElasticityHelper* CreateScrollElasticityHelper() = 0;
 
   // Called by the single-threaded UI Compositor to get or set the scroll offset
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index f8ee98f..016e2601 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -4,7 +4,9 @@
 
 #include "cc/metrics/compositor_frame_reporter.h"
 
+#include <memory>
 #include <string>
+#include <utility>
 
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
@@ -13,6 +15,7 @@
 #include "cc/base/rolling_time_delta_history.h"
 #include "cc/metrics/frame_sequence_tracker.h"
 #include "cc/metrics/latency_ukm_reporter.h"
+#include "ui/events/types/event_type.h"
 
 namespace cc {
 namespace {
@@ -126,21 +129,28 @@
 // This value should be recalculated in case of changes to the number of values
 // in CompositorFrameReporter::DroppedFrameReportType or in
 // CompositorFrameReporter::StageType
-constexpr int kMaxHistogramIndex = kDroppedFrameReportTypeCount *
-                                   kFrameSequenceTrackerTypeCount *
-                                   (kStageTypeCount + kAllBreakdownCount);
-constexpr int kHistogramMin = 1;
-constexpr int kHistogramMax = 350000;
-constexpr int kHistogramBucketCount = 50;
+constexpr int kMaxCompositorLatencyHistogramIndex =
+    kDroppedFrameReportTypeCount * kFrameSequenceTrackerTypeCount *
+    (kStageTypeCount + kAllBreakdownCount);
+constexpr int kCompositorLatencyHistogramMin = 1;
+constexpr int kCompositorLatencyHistogramMax = 350000;
+constexpr int kCompositorLatencyHistogramBucketCount = 50;
+
+constexpr int kMaxEventLatencyHistogramIndex =
+    static_cast<int>(ui::EventType::ET_LAST);
+constexpr int kEventLatencyHistogramMin = 1;
+constexpr int kEventLatencyHistogramMax = 5000000;
+constexpr int kEventLatencyHistogramBucketCount = 100;
 
 bool ShouldReportLatencyMetricsForSequenceType(
     FrameSequenceTrackerType sequence_type) {
   return sequence_type != FrameSequenceTrackerType::kUniversal;
 }
 
-std::string HistogramName(const int report_type_index,
-                          FrameSequenceTrackerType frame_sequence_tracker_type,
-                          const int stage_type_index) {
+std::string GetCompositorLatencyHistogramName(
+    const int report_type_index,
+    FrameSequenceTrackerType frame_sequence_tracker_type,
+    const int stage_type_index) {
   DCHECK_LE(frame_sequence_tracker_type, FrameSequenceTrackerType::kMaxType);
   DCHECK(
       ShouldReportLatencyMetricsForSequenceType(frame_sequence_tracker_type));
@@ -154,6 +164,11 @@
                        GetStageName(stage_type_index)});
 }
 
+std::string GetEventLatencyHistogramName(const EventMetrics& event_metrics) {
+  return base::StrCat(
+      {"EventLatency.", event_metrics.GetTypeName(), ".TotalLatency"});
+}
+
 }  // namespace
 
 CompositorFrameReporter::CompositorFrameReporter(
@@ -257,6 +272,12 @@
   viz_breakdown_ = viz_breakdown;
 }
 
+void CompositorFrameReporter::SetEventsMetrics(
+    std::vector<EventMetrics> events_metrics) {
+  DCHECK_EQ(0u, events_metrics_.size());
+  events_metrics_ = std::move(events_metrics);
+}
+
 void CompositorFrameReporter::DroppedFrame() {
   report_type_ = DroppedFrameReportType::kDroppedFrame;
 }
@@ -265,20 +286,22 @@
   if (frame_termination_status_ == FrameTerminationStatus::kUnknown)
     TerminateFrame(FrameTerminationStatus::kUnknown, base::TimeTicks::Now());
   DCHECK_EQ(current_stage_.start_time, base::TimeTicks());
-  bool report_latency = false;
+  bool report_compositor_latency = false;
+  bool report_event_latency = false;
   const char* termination_status_str = nullptr;
   switch (frame_termination_status_) {
     case FrameTerminationStatus::kPresentedFrame:
-      report_latency = true;
+      report_compositor_latency = true;
+      report_event_latency = true;
       termination_status_str = "presented_frame";
       break;
     case FrameTerminationStatus::kDidNotPresentFrame:
-      report_latency = true;
+      report_compositor_latency = true;
       DroppedFrame();
       termination_status_str = "did_not_present_frame";
       break;
     case FrameTerminationStatus::kReplacedByNewReporter:
-      report_latency = true;
+      report_compositor_latency = true;
       DroppedFrame();
       termination_status_str = "replaced_by_new_reporter_at_same_stage";
       break;
@@ -303,17 +326,20 @@
         "compositor_frame_submission_status", submission_status_str);
   }
 
-  // Only report histograms if the frame was presented.
-  if (report_latency) {
+  // Only report compositor latency histograms if the frame was produced.
+  if (report_compositor_latency) {
     DCHECK(stage_history_.size());
     stage_history_.emplace_back(StageType::kTotalLatency,
                                 stage_history_.front().start_time,
                                 stage_history_.back().end_time);
-    ReportStageHistograms();
+    ReportCompositorLatencyHistograms();
   }
+  // Only report event latency histograms if the frame was presented.
+  if (report_event_latency)
+    ReportEventLatencyHistograms();
 }
 
-void CompositorFrameReporter::ReportStageHistograms() const {
+void CompositorFrameReporter::ReportCompositorLatencyHistograms() const {
   for (const StageData& stage : stage_history_) {
     ReportStageHistogramWithBreakdown(stage);
 
@@ -334,8 +360,9 @@
   if (!ShouldReportLatencyMetricsForSequenceType(frame_sequence_tracker_type))
     return;
   base::TimeDelta stage_delta = stage.end_time - stage.start_time;
-  ReportHistogram(frame_sequence_tracker_type,
-                  static_cast<int>(stage.stage_type), stage_delta);
+  ReportCompositorLatencyHistogram(frame_sequence_tracker_type,
+                                   static_cast<int>(stage.stage_type),
+                                   stage_delta);
   switch (stage.stage_type) {
     case StageType::kSendBeginMainFrameToCommit:
       ReportBlinkBreakdowns(stage.start_time, frame_sequence_tracker_type);
@@ -368,9 +395,10 @@
        begin_main_frame_start_ - start_time}};
 
   for (const auto& pair : breakdowns) {
-    ReportHistogram(frame_sequence_tracker_type,
-                    kBlinkBreakdownInitialIndex + static_cast<int>(pair.first),
-                    pair.second);
+    ReportCompositorLatencyHistogram(
+        frame_sequence_tracker_type,
+        kBlinkBreakdownInitialIndex + static_cast<int>(pair.first),
+        pair.second);
   }
 }
 
@@ -386,7 +414,7 @@
   }
   base::TimeDelta submit_to_receive_compositor_frame_delta =
       viz_breakdown_.received_compositor_frame_timestamp - start_time;
-  ReportHistogram(
+  ReportCompositorLatencyHistogram(
       frame_sequence_tracker_type,
       kVizBreakdownInitialIndex +
           static_cast<int>(VizBreakdown::kSubmitToReceiveCompositorFrame),
@@ -397,7 +425,7 @@
   base::TimeDelta received_compositor_frame_to_start_draw_delta =
       viz_breakdown_.draw_start_timestamp -
       viz_breakdown_.received_compositor_frame_timestamp;
-  ReportHistogram(
+  ReportCompositorLatencyHistogram(
       frame_sequence_tracker_type,
       kVizBreakdownInitialIndex +
           static_cast<int>(VizBreakdown::kReceivedCompositorFrameToStartDraw),
@@ -409,32 +437,34 @@
       viz_breakdown_.swap_timings.swap_start -
       viz_breakdown_.draw_start_timestamp;
 
-  ReportHistogram(frame_sequence_tracker_type,
-                  kVizBreakdownInitialIndex +
-                      static_cast<int>(VizBreakdown::kStartDrawToSwapStart),
-                  start_draw_to_swap_start_delta);
+  ReportCompositorLatencyHistogram(
+      frame_sequence_tracker_type,
+      kVizBreakdownInitialIndex +
+          static_cast<int>(VizBreakdown::kStartDrawToSwapStart),
+      start_draw_to_swap_start_delta);
 
   base::TimeDelta swap_start_to_swap_end_delta =
       viz_breakdown_.swap_timings.swap_end -
       viz_breakdown_.swap_timings.swap_start;
 
-  ReportHistogram(frame_sequence_tracker_type,
-                  kVizBreakdownInitialIndex +
-                      static_cast<int>(VizBreakdown::kSwapStartToSwapEnd),
-                  swap_start_to_swap_end_delta);
+  ReportCompositorLatencyHistogram(
+      frame_sequence_tracker_type,
+      kVizBreakdownInitialIndex +
+          static_cast<int>(VizBreakdown::kSwapStartToSwapEnd),
+      swap_start_to_swap_end_delta);
 
   base::TimeDelta swap_end_to_presentation_compositor_frame_delta =
       viz_breakdown_.presentation_feedback.timestamp -
       viz_breakdown_.swap_timings.swap_end;
 
-  ReportHistogram(
+  ReportCompositorLatencyHistogram(
       frame_sequence_tracker_type,
       kVizBreakdownInitialIndex +
           static_cast<int>(VizBreakdown::kSwapEndToPresentationCompositorFrame),
       swap_end_to_presentation_compositor_frame_delta);
 }
 
-void CompositorFrameReporter::ReportHistogram(
+void CompositorFrameReporter::ReportCompositorLatencyHistogram(
     FrameSequenceTrackerType frame_sequence_tracker_type,
     const int stage_type_index,
     base::TimeDelta time_delta) const {
@@ -451,18 +481,37 @@
   CHECK_GE(stage_type_index, 0);
   CHECK_LT(report_type_index, kDroppedFrameReportTypeCount);
   CHECK_GE(report_type_index, 0);
-  CHECK_LT(histogram_index, kMaxHistogramIndex);
+  CHECK_LT(histogram_index, kMaxCompositorLatencyHistogramIndex);
   CHECK_GE(histogram_index, 0);
 
   STATIC_HISTOGRAM_POINTER_GROUP(
-      HistogramName(report_type_index, frame_sequence_tracker_type,
-                    stage_type_index),
-      histogram_index, kMaxHistogramIndex,
+      GetCompositorLatencyHistogramName(
+          report_type_index, frame_sequence_tracker_type, stage_type_index),
+      histogram_index, kMaxCompositorLatencyHistogramIndex,
       AddTimeMicrosecondsGranularity(time_delta),
       base::Histogram::FactoryGet(
-          HistogramName(report_type_index, frame_sequence_tracker_type,
-                        stage_type_index),
-          kHistogramMin, kHistogramMax, kHistogramBucketCount,
+          GetCompositorLatencyHistogramName(
+              report_type_index, frame_sequence_tracker_type, stage_type_index),
+          kCompositorLatencyHistogramMin, kCompositorLatencyHistogramMax,
+          kCompositorLatencyHistogramBucketCount,
           base::HistogramBase::kUmaTargetedHistogramFlag));
 }
+
+void CompositorFrameReporter::ReportEventLatencyHistograms() const {
+  for (const EventMetrics& event_metrics : events_metrics_) {
+    base::TimeDelta total_latency =
+        frame_termination_time_ - event_metrics.time_stamp();
+    const int type_index = static_cast<int>(event_metrics.type());
+    STATIC_HISTOGRAM_POINTER_GROUP(
+        GetEventLatencyHistogramName(event_metrics), type_index,
+        kMaxEventLatencyHistogramIndex,
+        AddTimeMicrosecondsGranularity(total_latency),
+        base::Histogram::FactoryGet(
+            GetEventLatencyHistogramName(event_metrics),
+            kEventLatencyHistogramMin, kEventLatencyHistogramMax,
+            kEventLatencyHistogramBucketCount,
+            base::HistogramBase::kUmaTargetedHistogramFlag));
+  }
+}
+
 }  // namespace cc
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index 922c0c3..477be2d 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -5,6 +5,7 @@
 #ifndef CC_METRICS_COMPOSITOR_FRAME_REPORTER_H_
 #define CC_METRICS_COMPOSITOR_FRAME_REPORTER_H_
 
+#include <memory>
 #include <vector>
 
 #include "base/containers/flat_set.h"
@@ -12,6 +13,7 @@
 #include "cc/base/base_export.h"
 #include "cc/cc_export.h"
 #include "cc/metrics/begin_main_frame_metrics.h"
+#include "cc/metrics/event_metrics.h"
 #include "cc/metrics/frame_sequence_tracker.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/frame_timing_details.h"
@@ -137,6 +139,7 @@
   void SetBlinkBreakdown(std::unique_ptr<BeginMainFrameMetrics> blink_breakdown,
                          base::TimeTicks begin_main_start);
   void SetVizBreakdown(const viz::FrameTimingDetails& viz_breakdown);
+  void SetEventsMetrics(std::vector<EventMetrics> events_metrics);
 
   int StageHistorySizeForTesting() { return stage_history_.size(); }
 
@@ -153,7 +156,7 @@
 
   void TerminateReporter();
   void EndCurrentStage(base::TimeTicks end_time);
-  void ReportStageHistograms() const;
+  void ReportCompositorLatencyHistograms() const;
   void ReportStageHistogramWithBreakdown(
       const StageData& stage,
       FrameSequenceTrackerType frame_sequence_tracker_type =
@@ -164,9 +167,11 @@
   void ReportVizBreakdowns(
       const base::TimeTicks start_time,
       FrameSequenceTrackerType frame_sequence_tracker_type) const;
-  void ReportHistogram(FrameSequenceTrackerType intraction_type,
-                       const int stage_type_index,
-                       base::TimeDelta time_delta) const;
+  void ReportCompositorLatencyHistogram(
+      FrameSequenceTrackerType intraction_type,
+      const int stage_type_index,
+      base::TimeDelta time_delta) const;
+  void ReportEventLatencyHistograms() const;
 
   StageData current_stage_;
   BeginMainFrameMetrics blink_breakdown_;
@@ -177,6 +182,9 @@
   // be divided based on the frame submission status.
   std::vector<StageData> stage_history_;
 
+  // List of metrics for events affecting this frame.
+  std::vector<EventMetrics> events_metrics_;
+
   const bool is_single_threaded_;
   DroppedFrameReportType report_type_ =
       DroppedFrameReportType::kNonDroppedFrame;
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc
index 9e69117..0e91673 100644
--- a/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -4,14 +4,24 @@
 
 #include "cc/metrics/compositor_frame_reporter.h"
 
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/strings/strcat.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "cc/metrics/compositor_frame_reporting_controller.h"
+#include "cc/metrics/event_metrics.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cc {
 namespace {
 
-class CompositorFrameReporterTest;
+MATCHER(IsWhitelisted,
+        base::StrCat({negation ? "isn't" : "is", " whitelisted"})) {
+  return arg.IsWhitelisted();
+}
 
 class CompositorFrameReporterTest : public testing::Test {
  public:
@@ -177,5 +187,97 @@
   histogram_tester.ExpectBucketCount(
       "CompositorLatency.DroppedFrame.TotalLatency", 5, 1);
 }
+
+// Tests that when a frame is presented to the user, event latency metrics are
+// reported properly.
+TEST_F(CompositorFrameReporterTest, EventLatencyForPresentedFrameReported) {
+  base::HistogramTester histogram_tester;
+
+  const base::TimeTicks event_time = Now();
+  std::vector<EventMetrics> events_metrics = {
+      {ui::ET_TOUCH_PRESSED, event_time},
+      {ui::ET_TOUCH_MOVED, event_time},
+      {ui::ET_TOUCH_MOVED, event_time},
+  };
+  EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted()));
+
+  AdvanceNowByMs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
+      Now());
+
+  AdvanceNowByMs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
+      Now());
+
+  AdvanceNowByMs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::
+          kSubmitCompositorFrameToPresentationCompositorFrame,
+      Now());
+  pipeline_reporter_->SetEventsMetrics(std::move(events_metrics));
+
+  AdvanceNowByMs(3);
+  const base::TimeTicks presentation_time = Now();
+  pipeline_reporter_->TerminateFrame(
+      CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
+      presentation_time);
+
+  pipeline_reporter_ = nullptr;
+
+  const int latency_ms = (presentation_time - event_time).InMicroseconds();
+  histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency",
+                                    1);
+  histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 2);
+  histogram_tester.ExpectBucketCount("EventLatency.TouchPressed.TotalLatency",
+                                     latency_ms, 1);
+  histogram_tester.ExpectBucketCount("EventLatency.TouchMoved.TotalLatency",
+                                     latency_ms, 2);
+}
+
+// Tests that when the frame is not presented to the user, event latency metrics
+// are not reported.
+TEST_F(CompositorFrameReporterTest,
+       EventLatencyForDidNotPresentFrameNotReported) {
+  base::HistogramTester histogram_tester;
+
+  const base::TimeTicks event_time = Now();
+  std::vector<EventMetrics> events_metrics = {
+      {ui::ET_TOUCH_PRESSED, event_time},
+      {ui::ET_TOUCH_MOVED, event_time},
+      {ui::ET_TOUCH_MOVED, event_time},
+  };
+  EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted()));
+
+  AdvanceNowByMs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
+      Now());
+
+  AdvanceNowByMs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
+      Now());
+
+  AdvanceNowByMs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::
+          kSubmitCompositorFrameToPresentationCompositorFrame,
+      Now());
+  pipeline_reporter_->SetEventsMetrics(std::move(events_metrics));
+
+  AdvanceNowByMs(3);
+  pipeline_reporter_->TerminateFrame(
+      CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame,
+      Now());
+
+  pipeline_reporter_ = nullptr;
+
+  histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency",
+                                    0);
+  histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 0);
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/metrics/compositor_frame_reporting_controller.cc b/cc/metrics/compositor_frame_reporting_controller.cc
index 2df9ec77..e5dcd52 100644
--- a/cc/metrics/compositor_frame_reporting_controller.cc
+++ b/cc/metrics/compositor_frame_reporting_controller.cc
@@ -4,6 +4,8 @@
 
 #include "cc/metrics/compositor_frame_reporting_controller.h"
 
+#include <utility>
+
 #include "cc/metrics/compositor_frame_reporter.h"
 #include "cc/metrics/latency_ukm_reporter.h"
 #include "components/viz/common/frame_timing_details.h"
@@ -142,7 +144,8 @@
 void CompositorFrameReportingController::DidSubmitCompositorFrame(
     uint32_t frame_token,
     const viz::BeginFrameId& current_frame_id,
-    const viz::BeginFrameId& last_activated_frame_id) {
+    const viz::BeginFrameId& last_activated_frame_id,
+    std::vector<EventMetrics> events_metrics) {
   // If the last_activated_frame_id from scheduler is the same as
   // last_submitted_frame_id_ in reporting controller, this means that we are
   // submitting the Impl frame. In this case the frame will be submitted if
@@ -179,6 +182,7 @@
       std::move(reporters_[PipelineStage::kActivate]);
   submitted_reporter->StartStage(
       StageType::kSubmitCompositorFrameToPresentationCompositorFrame, Now());
+  submitted_reporter->SetEventsMetrics(std::move(events_metrics));
   submitted_compositor_frames_.emplace_back(frame_token,
                                             std::move(submitted_reporter));
 }
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index 9f78fa4..8cab7e1 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -13,6 +13,7 @@
 #include "cc/base/rolling_time_delta_history.h"
 #include "cc/cc_export.h"
 #include "cc/metrics/compositor_frame_reporter.h"
+#include "cc/metrics/event_metrics.h"
 #include "cc/metrics/frame_sequence_tracker.h"
 
 namespace viz {
@@ -60,7 +61,8 @@
   virtual void DidSubmitCompositorFrame(
       uint32_t frame_token,
       const viz::BeginFrameId& current_frame_id,
-      const viz::BeginFrameId& last_activated_frame_id);
+      const viz::BeginFrameId& last_activated_frame_id,
+      std::vector<EventMetrics> events_metrics);
   virtual void DidNotProduceFrame(const viz::BeginFrameId& id);
   virtual void OnFinishImplFrame(const viz::BeginFrameId& id);
   virtual void DidPresentCompositorFrame(
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index ea2b5ef..dd96349 100644
--- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -4,23 +4,30 @@
 
 #include "cc/metrics/compositor_frame_reporting_controller.h"
 
+#include <utility>
+#include <vector>
+
 #include "base/macros.h"
+#include "base/strings/strcat.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "cc/metrics/event_metrics.h"
 #include "components/viz/common/frame_timing_details.h"
 #include "components/viz/common/quads/compositor_frame_metadata.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cc {
 namespace {
 
-class CompositorFrameReportingControllerTest;
+MATCHER(IsWhitelisted,
+        base::StrCat({negation ? "isn't" : "is", " whitelisted"})) {
+  return arg.IsWhitelisted();
+}
 
 class TestCompositorFrameReportingController
     : public CompositorFrameReportingController {
  public:
-  TestCompositorFrameReportingController(
-      CompositorFrameReportingControllerTest* test)
-      : CompositorFrameReportingController(), test_(test) {}
+  TestCompositorFrameReportingController() = default;
 
   TestCompositorFrameReportingController(
       const TestCompositorFrameReportingController& controller) = delete;
@@ -38,16 +45,11 @@
     }
     return count;
   }
-
- protected:
-  CompositorFrameReportingControllerTest* test_;
 };
 
 class CompositorFrameReportingControllerTest : public testing::Test {
  public:
-  CompositorFrameReportingControllerTest() : reporting_controller_(this) {
-    current_id_ = viz::BeginFrameId(1, 1);
-  }
+  CompositorFrameReportingControllerTest() : current_id_(1, 1) {}
 
   // The following functions simulate the actions that would
   // occur for each phase of the reporting controller.
@@ -89,34 +91,35 @@
               [CompositorFrameReportingController::PipelineStage::kCommit]);
     reporting_controller_.WillActivate();
     reporting_controller_.DidActivate();
-    last_activated_id_ = viz::BeginFrameId(current_id_);
+    last_activated_id_ = current_id_;
   }
 
-  void SimulateSubmitCompositorFrame(uint32_t frame_token) {
+  void SimulateSubmitCompositorFrame(uint32_t frame_token,
+                                     std::vector<EventMetrics> events_metrics) {
     if (!reporting_controller_.reporters()
              [CompositorFrameReportingController::PipelineStage::kActivate])
       SimulateActivate();
     CHECK(reporting_controller_.reporters()
               [CompositorFrameReportingController::PipelineStage::kActivate]);
-    reporting_controller_.DidSubmitCompositorFrame(frame_token, current_id_,
-                                                   last_activated_id_);
+    reporting_controller_.DidSubmitCompositorFrame(
+        frame_token, current_id_, last_activated_id_, events_metrics);
   }
 
   void SimulatePresentCompositorFrame() {
     ++next_token_;
-    SimulateSubmitCompositorFrame(*next_token_);
+    SimulateSubmitCompositorFrame(*next_token_, std::vector<EventMetrics>());
     viz::FrameTimingDetails details = {};
     details.presentation_feedback.timestamp = base::TimeTicks::Now();
     reporting_controller_.DidPresentCompositorFrame(*next_token_, details);
   }
 
+  void IncrementCurrentId() { current_id_.sequence_number++; }
+
  protected:
   TestCompositorFrameReportingController reporting_controller_;
   viz::BeginFrameId current_id_;
   viz::BeginFrameId last_activated_id_;
   base::TimeTicks begin_main_start_;
-
- private:
   viz::FrameTokenGenerator next_token_;
 };
 
@@ -157,15 +160,15 @@
   reporting_controller_.WillBeginMainFrame(current_id_);
   reporting_controller_.WillActivate();
   reporting_controller_.DidActivate();
-  last_activated_id_ = viz::BeginFrameId(current_id_);
+  last_activated_id_ = current_id_;
   reporting_controller_.WillCommit();
   reporting_controller_.DidCommit();
   reporting_controller_.WillActivate();
   reporting_controller_.DidActivate();
   EXPECT_EQ(1, reporting_controller_.ActiveReporters());
 
-  reporting_controller_.DidSubmitCompositorFrame(0, current_id_,
-                                                 last_activated_id_);
+  reporting_controller_.DidSubmitCompositorFrame(
+      0, current_id_, last_activated_id_, std::vector<EventMetrics>());
   EXPECT_EQ(0, reporting_controller_.ActiveReporters());
 
   // 4 simultaneous reporters active.
@@ -256,9 +259,9 @@
 
 TEST_F(CompositorFrameReportingControllerTest, MainFrameCausedNoDamage) {
   base::HistogramTester histogram_tester;
-  viz::BeginFrameId current_id_1_ = viz::BeginFrameId(1, 1);
-  viz::BeginFrameId current_id_2_ = viz::BeginFrameId(1, 2);
-  viz::BeginFrameId current_id_3_ = viz::BeginFrameId(1, 3);
+  viz::BeginFrameId current_id_1_(1, 1);
+  viz::BeginFrameId current_id_2_(1, 2);
+  viz::BeginFrameId current_id_3_(1, 3);
 
   reporting_controller_.WillBeginImplFrame(current_id_1_);
   reporting_controller_.WillBeginMainFrame(current_id_1_);
@@ -288,8 +291,8 @@
   reporting_controller_.WillBeginMainFrame(current_id_);
   reporting_controller_.BeginMainFrameAborted(current_id_);
   reporting_controller_.OnFinishImplFrame(current_id_);
-  reporting_controller_.DidSubmitCompositorFrame(1, current_id_,
-                                                 last_activated_id_);
+  reporting_controller_.DidSubmitCompositorFrame(
+      1, current_id_, last_activated_id_, std::vector<EventMetrics>());
 
   viz::FrameTimingDetails details = {};
   reporting_controller_.DidPresentCompositorFrame(1, details);
@@ -307,9 +310,9 @@
 
 TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted2) {
   base::HistogramTester histogram_tester;
-  viz::BeginFrameId current_id_1_ = viz::BeginFrameId(1, 1);
-  viz::BeginFrameId current_id_2_ = viz::BeginFrameId(1, 2);
-  viz::BeginFrameId current_id_3_ = viz::BeginFrameId(1, 3);
+  viz::BeginFrameId current_id_1_(1, 1);
+  viz::BeginFrameId current_id_2_(1, 2);
+  viz::BeginFrameId current_id_3_(1, 3);
   reporting_controller_.WillBeginImplFrame(current_id_1_);
   reporting_controller_.OnFinishImplFrame(current_id_1_);
   reporting_controller_.WillBeginMainFrame(current_id_1_);
@@ -321,8 +324,8 @@
   reporting_controller_.WillBeginMainFrame(current_id_2_);
   reporting_controller_.OnFinishImplFrame(current_id_2_);
   reporting_controller_.BeginMainFrameAborted(current_id_2_);
-  reporting_controller_.DidSubmitCompositorFrame(1, current_id_2_,
-                                                 current_id_1_);
+  reporting_controller_.DidSubmitCompositorFrame(
+      1, current_id_2_, current_id_1_, std::vector<EventMetrics>());
   viz::FrameTimingDetails details = {};
   reporting_controller_.DidPresentCompositorFrame(1, details);
   histogram_tester.ExpectTotalCount(
@@ -340,8 +343,8 @@
   histogram_tester.ExpectTotalCount(
       "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
       1);
-  reporting_controller_.DidSubmitCompositorFrame(2, current_id_2_,
-                                                 current_id_1_);
+  reporting_controller_.DidSubmitCompositorFrame(
+      2, current_id_2_, current_id_1_, std::vector<EventMetrics>());
   reporting_controller_.DidPresentCompositorFrame(2, details);
   histogram_tester.ExpectTotalCount(
       "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
@@ -360,8 +363,8 @@
       2);
   reporting_controller_.WillBeginImplFrame(current_id_3_);
   reporting_controller_.OnFinishImplFrame(current_id_3_);
-  reporting_controller_.DidSubmitCompositorFrame(3, current_id_3_,
-                                                 current_id_1_);
+  reporting_controller_.DidSubmitCompositorFrame(
+      3, current_id_3_, current_id_1_, std::vector<EventMetrics>());
   reporting_controller_.DidPresentCompositorFrame(3, details);
   histogram_tester.ExpectTotalCount(
       "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
@@ -436,5 +439,76 @@
       "CompositorLatency.SendBeginMainFrameToCommit.BeginMainSentToStarted", 1);
 }
 
+// Tests that EventLatency histograms are reported properly when a frame is
+// presented to the user.
+TEST_F(CompositorFrameReportingControllerTest,
+       EventLatencyForPresentedFrameReported) {
+  base::HistogramTester histogram_tester;
+
+  const base::TimeTicks event_time = base::TimeTicks::Now();
+  std::vector<EventMetrics> events_metrics = {
+      {ui::ET_TOUCH_PRESSED, event_time},
+      {ui::ET_TOUCH_MOVED, event_time},
+      {ui::ET_TOUCH_MOVED, event_time},
+  };
+  EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted()));
+
+  // Submit a compositor frame and notify CompositorFrameReporter of the events
+  // affecting the frame.
+  ++next_token_;
+  SimulateSubmitCompositorFrame(*next_token_, events_metrics);
+
+  // Present the submitted compositor frame to the user.
+  const base::TimeTicks presentation_time = base::TimeTicks::Now();
+  viz::FrameTimingDetails details;
+  details.presentation_feedback.timestamp = presentation_time;
+  reporting_controller_.DidPresentCompositorFrame(*next_token_, details);
+
+  // Verify that EventLatency histograms are recorded.
+  const int latency_ms = (presentation_time - event_time).InMicroseconds();
+  histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency",
+                                    1);
+  histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 2);
+  histogram_tester.ExpectBucketCount("EventLatency.TouchPressed.TotalLatency",
+                                     latency_ms, 1);
+  histogram_tester.ExpectBucketCount("EventLatency.TouchMoved.TotalLatency",
+                                     latency_ms, 2);
+}
+
+// Tests that EventLatency histograms are not reported when the frame is dropped
+// and not presented to the user.
+TEST_F(CompositorFrameReportingControllerTest,
+       EventLatencyForDidNotPresentFrameNotReported) {
+  base::HistogramTester histogram_tester;
+
+  const base::TimeTicks event_time = base::TimeTicks::Now();
+  std::vector<EventMetrics> events_metrics = {
+      {ui::ET_TOUCH_PRESSED, event_time},
+      {ui::ET_TOUCH_MOVED, event_time},
+      {ui::ET_TOUCH_MOVED, event_time},
+  };
+  EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted()));
+
+  // Submit a compositor frame and notify CompositorFrameReporter of the events
+  // affecting the frame.
+  ++next_token_;
+  SimulateSubmitCompositorFrame(*next_token_, events_metrics);
+
+  // Submit another compositor frame.
+  ++next_token_;
+  IncrementCurrentId();
+  SimulateSubmitCompositorFrame(*next_token_, std::vector<EventMetrics>());
+
+  // Present the second compositor frame to the uesr, dropping the first one.
+  viz::FrameTimingDetails details;
+  details.presentation_feedback.timestamp = base::TimeTicks::Now();
+  reporting_controller_.DidPresentCompositorFrame(*next_token_, details);
+
+  // Verify that no EventLatency histogram is recorded.
+  histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency",
+                                    0);
+  histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 0);
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/metrics/compositor_timing_history.cc b/cc/metrics/compositor_timing_history.cc
index 8e2d0c12..91e8fc8 100644
--- a/cc/metrics/compositor_timing_history.cc
+++ b/cc/metrics/compositor_timing_history.cc
@@ -6,6 +6,9 @@
 
 #include <stddef.h>
 #include <stdint.h>
+#include <algorithm>
+#include <utility>
+#include <vector>
 
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
@@ -979,10 +982,12 @@
 void CompositorTimingHistory::DidSubmitCompositorFrame(
     uint32_t frame_token,
     const viz::BeginFrameId& current_frame_id,
-    const viz::BeginFrameId& last_activated_frame_id) {
+    const viz::BeginFrameId& last_activated_frame_id,
+    std::vector<EventMetrics> events_metrics) {
   DCHECK_EQ(base::TimeTicks(), submit_start_time_);
   compositor_frame_reporting_controller_->DidSubmitCompositorFrame(
-      frame_token, current_frame_id, last_activated_frame_id);
+      frame_token, current_frame_id, last_activated_frame_id,
+      std::move(events_metrics));
   submit_start_time_ = Now();
 }
 
diff --git a/cc/metrics/compositor_timing_history.h b/cc/metrics/compositor_timing_history.h
index 8f7c267c..613f007 100644
--- a/cc/metrics/compositor_timing_history.h
+++ b/cc/metrics/compositor_timing_history.h
@@ -6,9 +6,11 @@
 #define CC_METRICS_COMPOSITOR_TIMING_HISTORY_H_
 
 #include <memory>
+#include <vector>
 
 #include "cc/base/rolling_time_delta_history.h"
 #include "cc/cc_export.h"
+#include "cc/metrics/event_metrics.h"
 #include "cc/tiles/tile_priority.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 
@@ -98,7 +100,8 @@
   void DidSubmitCompositorFrame(
       uint32_t frame_token,
       const viz::BeginFrameId& current_frame_id,
-      const viz::BeginFrameId& last_activated_frame_id);
+      const viz::BeginFrameId& last_activated_frame_id,
+      std::vector<EventMetrics> events_metrics);
   void DidNotProduceFrame(const viz::BeginFrameId& id);
   void DidReceiveCompositorFrameAck();
   void DidPresentCompositorFrame(uint32_t frame_token,
diff --git a/cc/metrics/event_metrics.cc b/cc/metrics/event_metrics.cc
new file mode 100644
index 0000000..ebdddd7
--- /dev/null
+++ b/cc/metrics/event_metrics.cc
@@ -0,0 +1,64 @@
+// 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 <tuple>
+
+#include "base/stl_util.h"
+
+namespace cc {
+
+EventMetrics::EventMetrics(ui::EventType type, base::TimeTicks time_stamp)
+    : type_(type), time_stamp_(time_stamp) {}
+
+const char* EventMetrics::GetTypeName() const {
+  DCHECK(IsWhitelisted()) << "Event type is not whitelisted for event metrics: "
+                          << type_;
+
+  switch (type_) {
+    case ui::ET_MOUSE_PRESSED:
+      return "MousePressed";
+    case ui::ET_MOUSE_RELEASED:
+      return "MouseReleased";
+    case ui::ET_MOUSEWHEEL:
+      return "MouseWheel";
+    case ui::ET_KEY_PRESSED:
+      return "KeyPressed";
+    case ui::ET_KEY_RELEASED:
+      return "KeyReleased";
+    case ui::ET_TOUCH_PRESSED:
+      return "TouchPressed";
+    case ui::ET_TOUCH_RELEASED:
+      return "TouchReleased";
+    case ui::ET_TOUCH_MOVED:
+      return "TouchMoved";
+    default:
+      NOTREACHED();
+      return nullptr;
+  }
+}
+
+bool EventMetrics::IsWhitelisted() const {
+  switch (type_) {
+    case ui::ET_MOUSE_PRESSED:
+    case ui::ET_MOUSE_RELEASED:
+    case ui::ET_MOUSEWHEEL:
+    case ui::ET_KEY_PRESSED:
+    case ui::ET_KEY_RELEASED:
+    case ui::ET_TOUCH_PRESSED:
+    case ui::ET_TOUCH_RELEASED:
+    case ui::ET_TOUCH_MOVED:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool EventMetrics::operator==(const EventMetrics& other) const {
+  return std::tie(type_, time_stamp_) ==
+         std::tie(other.type_, other.time_stamp_);
+}
+
+}  // namespace cc
diff --git a/cc/metrics/event_metrics.h b/cc/metrics/event_metrics.h
new file mode 100644
index 0000000..ea3e78b
--- /dev/null
+++ b/cc/metrics/event_metrics.h
@@ -0,0 +1,34 @@
+// 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.
+
+#ifndef CC_METRICS_EVENT_METRICS_H_
+#define CC_METRICS_EVENT_METRICS_H_
+
+#include "base/time/time.h"
+#include "cc/cc_export.h"
+#include "ui/events/types/event_type.h"
+
+namespace cc {
+
+// Data about an event useful in generating event latency metrics.
+class CC_EXPORT EventMetrics {
+ public:
+  EventMetrics(ui::EventType type, base::TimeTicks time_stamp);
+
+  const char* GetTypeName() const;
+  bool IsWhitelisted() const;
+
+  ui::EventType type() const { return type_; }
+  base::TimeTicks time_stamp() const { return time_stamp_; }
+
+  bool operator==(const EventMetrics& other) const;
+
+ private:
+  ui::EventType type_;
+  base::TimeTicks time_stamp_;
+};
+
+}  // namespace cc
+
+#endif  // CC_METRICS_EVENT_METRICS_H_
diff --git a/cc/metrics/events_metrics_manager.cc b/cc/metrics/events_metrics_manager.cc
new file mode 100644
index 0000000..9303a2f
--- /dev/null
+++ b/cc/metrics/events_metrics_manager.cc
@@ -0,0 +1,65 @@
+// 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/events_metrics_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/stl_util.h"
+
+namespace cc {
+namespace {
+
+class ScopedMonitorImpl : public EventsMetricsManager::ScopedMonitor {
+ public:
+  ScopedMonitorImpl(
+      base::flat_map<ScopedMonitor*, EventMetrics>* events_metrics,
+      const EventMetrics& event_metrics)
+      : events_metrics_(events_metrics) {
+    events_metrics_->emplace(this, event_metrics);
+  }
+
+  ~ScopedMonitorImpl() override { events_metrics_->erase(this); }
+
+ private:
+  base::flat_map<ScopedMonitor*, EventMetrics>* const events_metrics_;
+};
+
+}  // namespace
+
+EventsMetricsManager::ScopedMonitor::~ScopedMonitor() = default;
+
+EventsMetricsManager::EventsMetricsManager() = default;
+EventsMetricsManager::~EventsMetricsManager() = default;
+
+std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+EventsMetricsManager::GetScopedMonitor(const EventMetrics& event_metrics) {
+  if (!event_metrics.IsWhitelisted())
+    return nullptr;
+  return std::make_unique<ScopedMonitorImpl>(&active_events_, event_metrics);
+}
+
+void EventsMetricsManager::SaveActiveEventsMetrics() {
+  saved_events_.reserve(saved_events_.size() + active_events_.size());
+  for (auto it = active_events_.begin(); it != active_events_.end();) {
+    saved_events_.push_back(it->second);
+    it = active_events_.erase(it);
+  }
+}
+
+std::vector<EventMetrics> EventsMetricsManager::TakeSavedEventsMetrics() {
+  std::vector<EventMetrics> result;
+  result.swap(saved_events_);
+  return result;
+}
+
+void EventsMetricsManager::AppendToSavedEventsMetrics(
+    std::vector<EventMetrics> events_metrics) {
+  saved_events_.reserve(saved_events_.size() + events_metrics.size());
+  saved_events_.insert(saved_events_.end(), events_metrics.begin(),
+                       events_metrics.end());
+}
+
+}  // namespace cc
diff --git a/cc/metrics/events_metrics_manager.h b/cc/metrics/events_metrics_manager.h
new file mode 100644
index 0000000..36d0818
--- /dev/null
+++ b/cc/metrics/events_metrics_manager.h
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef CC_METRICS_EVENTS_METRICS_MANAGER_H_
+#define CC_METRICS_EVENTS_METRICS_MANAGER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/time/time.h"
+#include "cc/cc_export.h"
+#include "cc/metrics/event_metrics.h"
+
+namespace cc {
+
+// Manages a list of active EventMetrics objects. Each thread (main or impl) has
+// its own instance of this class to help it determine which events have led to
+// a frame update.
+class CC_EXPORT EventsMetricsManager {
+ public:
+  // This interface is used to denote the scope of an event handling. The scope
+  // is started as soon as an instance is constructed and ended when instance is
+  // desctucted. EventsMetricsManager uses this to determine whether a frame
+  // update has happened due to handling of a specific event or not.
+  class ScopedMonitor {
+   public:
+    ScopedMonitor() = default;
+    virtual ~ScopedMonitor() = 0;
+
+    ScopedMonitor(const ScopedMonitor&) = delete;
+    ScopedMonitor& operator=(const ScopedMonitor&) = delete;
+  };
+
+  EventsMetricsManager();
+  ~EventsMetricsManager();
+
+  EventsMetricsManager(const EventsMetricsManager&) = delete;
+  EventsMetricsManager& operator=(const EventsMetricsManager&) = delete;
+
+  // Called by clients when they start handling an event. Destruction of the
+  // scoped monitor indicates the end of event handling.
+  std::unique_ptr<ScopedMonitor> GetScopedMonitor(
+      const EventMetrics& event_metrics);
+
+  // Called by clients when a frame needs to be produced. If any scoped monitor
+  // is active at this time, its corresponding event metrics would be saved.
+  void SaveActiveEventsMetrics();
+
+  // Empties the list of saved EventMetrics objects, returning them to the
+  // caller.
+  std::vector<EventMetrics> TakeSavedEventsMetrics();
+
+  void AppendToSavedEventsMetrics(std::vector<EventMetrics> events_metrics);
+
+ private:
+  // Map from scoped monitor to corresponding active EventMetrics.
+  base::flat_map<ScopedMonitor*, EventMetrics> active_events_;
+
+  // List of saved event metrics.
+  std::vector<EventMetrics> saved_events_;
+};
+
+}  // namespace cc
+
+#endif  // CC_METRICS_EVENTS_METRICS_MANAGER_H_
diff --git a/cc/metrics/events_metrics_manager_unittest.cc b/cc/metrics/events_metrics_manager_unittest.cc
new file mode 100644
index 0000000..02c057d
--- /dev/null
+++ b/cc/metrics/events_metrics_manager_unittest.cc
@@ -0,0 +1,90 @@
+// 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/events_metrics_manager.h"
+
+#include <utility>
+#include <vector>
+
+#include "cc/metrics/event_metrics.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/types/event_type.h"
+
+namespace cc {
+namespace {
+base::TimeTicks TimeAtMs(int ms) {
+  return base::TimeTicks() + base::TimeDelta::FromMilliseconds(ms);
+}
+}  // namespace
+
+class EventsMetricsManagerTest : public testing::Test {
+ public:
+  EventsMetricsManagerTest() = default;
+  ~EventsMetricsManagerTest() override = default;
+
+ protected:
+  EventsMetricsManager manager_;
+};
+
+// Tests that EventMetrics are saved only if they have a whitelisted event type
+// and SaveActiveEventsMetrics() is called inside their corresponding monitor's
+// scope.
+TEST_F(EventsMetricsManagerTest, EventsMetricsSaved) {
+  enum class Behavior {
+    kDoNotSave,
+    kSaveInsideScope,
+    kSaveOutsideScope,
+  };
+
+  std::vector<std::pair<EventMetrics, Behavior>> events = {
+      // A whitelisted event type for which SaveActiveEventsMetrics() is not
+      // called.
+      {{ui::ET_MOUSE_PRESSED, TimeAtMs(0)}, Behavior::kDoNotSave},
+
+      // A whitelisted event type for which SaveActiveEventsMetrics() is called
+      // inside its monitor scope.
+      {{ui::ET_MOUSE_PRESSED, TimeAtMs(1)}, Behavior::kSaveInsideScope},
+
+      // A whitelisted event type for which SaveActiveEventsMetrics() is called
+      // after its monitor scope is finished.
+      {{ui::ET_MOUSE_PRESSED, TimeAtMs(2)}, Behavior::kSaveOutsideScope},
+
+      // A non-whitelisted event type for which SaveActiveEventsMetrics() is
+      // called inside its monitor scope.
+      {{ui::ET_MOUSE_MOVED, TimeAtMs(3)}, Behavior::kSaveInsideScope},
+  };
+  EXPECT_TRUE(events[0].first.IsWhitelisted());
+  EXPECT_TRUE(events[1].first.IsWhitelisted());
+  EXPECT_TRUE(events[2].first.IsWhitelisted());
+  EXPECT_FALSE(events[3].first.IsWhitelisted());
+
+  // Out of the above events, only those with a whitelisted event type, for
+  // which SaveActiveEventsMetrics() is called inside its monitor scope, are
+  // expected to be saved.
+  std::vector<EventMetrics> expected_saved_events = {
+      events[1].first,
+  };
+
+  for (auto& event : events) {
+    {
+      auto monitor = manager_.GetScopedMonitor(event.first);
+      if (event.second == Behavior::kSaveInsideScope)
+        manager_.SaveActiveEventsMetrics();
+      // Ending the scope destroys the |monitor|.
+    }
+    if (event.second == Behavior::kSaveOutsideScope)
+      manager_.SaveActiveEventsMetrics();
+  }
+
+  // Check saved event metrics are as expected.
+  EXPECT_THAT(manager_.TakeSavedEventsMetrics(),
+              testing::ContainerEq(expected_saved_events));
+
+  // The first call to TakeSavedEventsMetrics() should remove events metrics
+  // from the manager, so the second call should return empty list.
+  EXPECT_THAT(manager_.TakeSavedEventsMetrics(), testing::IsEmpty());
+}
+
+}  // namespace cc
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 96b6ca8c77..326d7d92 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -5,6 +5,7 @@
 #include "cc/scheduler/scheduler.h"
 
 #include <algorithm>
+#include <vector>
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
@@ -145,10 +146,12 @@
   ProcessScheduledActions();
 }
 
-void Scheduler::DidSubmitCompositorFrame(uint32_t frame_token) {
+void Scheduler::DidSubmitCompositorFrame(
+    uint32_t frame_token,
+    std::vector<EventMetrics> events_metrics) {
   compositor_timing_history_->DidSubmitCompositorFrame(
       frame_token, begin_main_frame_args_.frame_id,
-      last_activate_origin_frame_args_.frame_id);
+      last_activate_origin_frame_args_.frame_id, std::move(events_metrics));
   state_machine_.DidSubmitCompositorFrame();
 
   // There is no need to call ProcessScheduledActions here because
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 02149ef..77c63e6 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -7,10 +7,12 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/cancelable_callback.h"
 #include "base/time/time.h"
 #include "cc/cc_export.h"
+#include "cc/metrics/event_metrics.h"
 #include "cc/scheduler/begin_frame_tracker.h"
 #include "cc/scheduler/draw_result.h"
 #include "cc/scheduler/scheduler.h"
@@ -170,7 +172,8 @@
 
   // Drawing should result in submitting a CompositorFrame to the
   // LayerTreeFrameSink and then calling this.
-  void DidSubmitCompositorFrame(uint32_t frame_token);
+  void DidSubmitCompositorFrame(uint32_t frame_token,
+                                std::vector<EventMetrics> events_metrics);
   // The LayerTreeFrameSink acks when it is ready for a new frame which
   // should result in this getting called to unblock the next draw.
   void DidReceiveCompositorFrameAck();
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 9a20d45..5da0a78 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/metrics/begin_main_frame_metrics.h"
+#include "cc/metrics/event_metrics.h"
 #include "cc/test/scheduler_test_common.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/test/begin_frame_args_test.h"
@@ -163,7 +164,7 @@
         draw_will_happen_ && swap_will_happen_if_draw_happens_;
     if (swap_will_happen) {
       last_begin_frame_ack_ = scheduler_->CurrentBeginFrameAckForActiveTree();
-      scheduler_->DidSubmitCompositorFrame(0);
+      scheduler_->DidSubmitCompositorFrame(0, std::vector<EventMetrics>());
 
       if (automatic_ack_)
         scheduler_->DidReceiveCompositorFrameAck();
diff --git a/cc/test/fake_compositor_frame_reporting_controller.cc b/cc/test/fake_compositor_frame_reporting_controller.cc
index 04e285f..b1183fe 100644
--- a/cc/test/fake_compositor_frame_reporting_controller.cc
+++ b/cc/test/fake_compositor_frame_reporting_controller.cc
@@ -3,6 +3,10 @@
 // found in the LICENSE file.
 
 #include "cc/test/fake_compositor_frame_reporting_controller.h"
+
+#include <utility>
+#include <vector>
+
 #include "components/viz/common/frame_timing_details.h"
 
 namespace cc {
@@ -51,9 +55,11 @@
 void FakeCompositorFrameReportingController::DidSubmitCompositorFrame(
     uint32_t frame_token,
     const viz::BeginFrameId& current_frame_id,
-    const viz::BeginFrameId& last_activated_frame_id) {
+    const viz::BeginFrameId& last_activated_frame_id,
+    std::vector<EventMetrics> events_metrics) {
   CompositorFrameReportingController::DidSubmitCompositorFrame(
-      frame_token, current_frame_id, last_activated_frame_id);
+      frame_token, current_frame_id, last_activated_frame_id,
+      std::move(events_metrics));
 
   viz::FrameTimingDetails details;
   details.presentation_feedback.timestamp = base::TimeTicks::Now();
diff --git a/cc/test/fake_compositor_frame_reporting_controller.h b/cc/test/fake_compositor_frame_reporting_controller.h
index 0536f550f77..b71c857 100644
--- a/cc/test/fake_compositor_frame_reporting_controller.h
+++ b/cc/test/fake_compositor_frame_reporting_controller.h
@@ -5,6 +5,8 @@
 #ifndef CC_TEST_FAKE_COMPOSITOR_FRAME_REPORTING_CONTROLLER_H_
 #define CC_TEST_FAKE_COMPOSITOR_FRAME_REPORTING_CONTROLLER_H_
 
+#include <vector>
+
 #include "cc/metrics/compositor_frame_reporting_controller.h"
 
 namespace viz {
@@ -36,7 +38,8 @@
   void DidSubmitCompositorFrame(
       uint32_t frame_token,
       const viz::BeginFrameId& current_frame_id,
-      const viz::BeginFrameId& last_activated_frame_id) override;
+      const viz::BeginFrameId& last_activated_frame_id,
+      std::vector<EventMetrics> events_metrics) override;
   void DidPresentCompositorFrame(
       uint32_t frame_token,
       const viz::FrameTimingDetails& details) override;
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 2a8622f..13573155 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -242,6 +242,16 @@
   return &swap_promise_manager_;
 }
 
+std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+LayerTreeHost::GetScopedEventMetricsMonitor(const EventMetrics& event_metrics) {
+  return events_metrics_manager_.GetScopedMonitor(event_metrics);
+}
+
+void LayerTreeHost::ClearEventsMetrics() {
+  // Take evens metrics and drop them.
+  events_metrics_manager_.TakeSavedEventsMetrics();
+}
+
 const LayerTreeSettings& LayerTreeHost::GetSettings() const {
   return settings_;
 }
@@ -348,6 +358,8 @@
     PushLayerTreeHostPropertiesTo(host_impl);
 
     sync_tree->PassSwapPromises(swap_promise_manager_.TakeSwapPromises());
+    host_impl->AppendEventsMetrics(
+        events_metrics_manager_.TakeSavedEventsMetrics());
 
     sync_tree->set_ui_resource_request_queue(
         ui_resource_manager_->TakeUIResourcesRequests());
@@ -590,17 +602,20 @@
 void LayerTreeHost::SetNeedsAnimate() {
   proxy_->SetNeedsAnimate();
   swap_promise_manager_.NotifySwapPromiseMonitorsOfSetNeedsCommit();
+  events_metrics_manager_.SaveActiveEventsMetrics();
 }
 
 DISABLE_CFI_PERF
 void LayerTreeHost::SetNeedsUpdateLayers() {
   proxy_->SetNeedsUpdateLayers();
   swap_promise_manager_.NotifySwapPromiseMonitorsOfSetNeedsCommit();
+  events_metrics_manager_.SaveActiveEventsMetrics();
 }
 
 void LayerTreeHost::SetNeedsCommit() {
   proxy_->SetNeedsCommit();
   swap_promise_manager_.NotifySwapPromiseMonitorsOfSetNeedsCommit();
+  events_metrics_manager_.SaveActiveEventsMetrics();
 }
 
 bool LayerTreeHost::RequestedMainFramePendingForTesting() {
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 943c40f..64812b6 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -34,6 +34,8 @@
 #include "cc/layers/layer_collections.h"
 #include "cc/layers/layer_list_iterator.h"
 #include "cc/metrics/begin_main_frame_metrics.h"
+#include "cc/metrics/event_metrics.h"
+#include "cc/metrics/events_metrics_manager.h"
 #include "cc/metrics/frame_sequence_tracker.h"
 #include "cc/paint/node_id.h"
 #include "cc/trees/browser_controls_params.h"
@@ -176,6 +178,10 @@
   // when a main frame is requested.
   SwapPromiseManager* GetSwapPromiseManager();
 
+  std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+  GetScopedEventMetricsMonitor(const EventMetrics& event_metrics);
+  void ClearEventsMetrics();
+
   // Visibility and LayerTreeFrameSink -------------------------------
 
   // Sets or gets if the LayerTreeHost is visible. When not visible it will:
@@ -899,6 +905,8 @@
   // result in a commit (due to no change in content).
   base::TimeTicks impl_commit_start_time_;
 
+  EventsMetricsManager events_metrics_manager_;
+
   // Used to vend weak pointers to LayerTreeHost to ScopedDeferMainFrameUpdate
   // objects.
   base::WeakPtrFactory<LayerTreeHost> defer_main_frame_update_weak_ptr_factory_{
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index a49da2c..c270e01 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -967,6 +967,12 @@
       new LatencyInfoSwapPromiseMonitor(latency, nullptr, this));
 }
 
+std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+LayerTreeHostImpl::GetScopedEventMetricsMonitor(
+    const EventMetrics& event_metrics) {
+  return events_metrics_manager_.GetScopedMonitor(event_metrics);
+}
+
 ScrollElasticityHelper* LayerTreeHostImpl::CreateScrollElasticityHelper() {
   DCHECK(!scroll_elasticity_helper_);
   if (settings_.enable_elastic_overscroll) {
@@ -3233,11 +3239,13 @@
   // SwapPromiseMonitor to say something happened that may cause a swap in the
   // future. The name should not refer to SetNeedsRedraw but it does for now.
   NotifySwapPromiseMonitorsOfSetNeedsRedraw();
+  events_metrics_manager_.SaveActiveEventsMetrics();
   client_->SetNeedsOneBeginImplFrameOnImplThread();
 }
 
 void LayerTreeHostImpl::SetNeedsRedraw() {
   NotifySwapPromiseMonitorsOfSetNeedsRedraw();
+  events_metrics_manager_.SaveActiveEventsMetrics();
   client_->SetNeedsRedrawOnImplThread();
 }
 
@@ -5860,6 +5868,7 @@
     // event, the LatencyInfo associated with the input event will not be
     // added as a swap promise and we won't get any swap results.
     NotifySwapPromiseMonitorsOfSetNeedsRedraw();
+    events_metrics_manager_.SaveActiveEventsMetrics();
 
     // The animation is no longer targeting a snap position. By clearing the
     // target, this will ensure that we attempt to resnap at the end of this
@@ -6171,6 +6180,15 @@
   client_->NeedsImplSideInvalidation(needs_first_draw_on_activation);
 }
 
+std::vector<EventMetrics> LayerTreeHostImpl::TakeEventsMetrics() {
+  return events_metrics_manager_.TakeSavedEventsMetrics();
+}
+
+void LayerTreeHostImpl::AppendEventsMetrics(
+    std::vector<EventMetrics> events_metrics) {
+  events_metrics_manager_.AppendToSavedEventsMetrics(std::move(events_metrics));
+}
+
 base::WeakPtr<LayerTreeHostImpl> LayerTreeHostImpl::AsWeakPtr() {
   return weak_factory_.GetWeakPtr();
 }
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 6b7938e..b9afa750 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -28,6 +28,8 @@
 #include "cc/input/scrollbar_animation_controller.h"
 #include "cc/input/scrollbar_controller.h"
 #include "cc/layers/layer_collections.h"
+#include "cc/metrics/event_metrics.h"
+#include "cc/metrics/events_metrics_manager.h"
 #include "cc/metrics/frame_sequence_tracker.h"
 #include "cc/paint/discardable_image_map.h"
 #include "cc/paint/paint_worklet_job.h"
@@ -301,6 +303,8 @@
       const gfx::Point& viewport_point) const override;
   std::unique_ptr<SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor(
       ui::LatencyInfo* latency) override;
+  std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+  GetScopedEventMetricsMonitor(const EventMetrics& event_metrics) override;
   ScrollElasticityHelper* CreateScrollElasticityHelper() override;
   bool GetScrollOffsetForLayer(ElementId element_id,
                                gfx::ScrollOffset* offset) override;
@@ -325,6 +329,9 @@
   void RequestBeginFrameForAnimatedImages() override;
   void RequestInvalidationForAnimatedImages() override;
 
+  std::vector<EventMetrics> TakeEventsMetrics();
+  void AppendEventsMetrics(std::vector<EventMetrics> events_metrics);
+
   base::WeakPtr<LayerTreeHostImpl> AsWeakPtr();
 
   void set_resourceless_software_draw_for_testing() {
@@ -1341,6 +1348,8 @@
   // Helper for de-jelly logic.
   DeJellyState de_jelly_state_;
 
+  EventsMetricsManager events_metrics_manager_;
+
   // Must be the last member to ensure this is destroyed first in the
   // destruction order and invalidates all weak pointers.
   base::WeakPtrFactory<LayerTreeHostImpl> weak_factory_{this};
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc
index 2ad76b8..fa484a8a 100644
--- a/cc/trees/proxy_impl.cc
+++ b/cc/trees/proxy_impl.cc
@@ -728,7 +728,8 @@
     if (host_impl_->DrawLayers(&frame)) {
       DCHECK_NE(frame.frame_token, 0u);
       // Drawing implies we submitted a frame to the LayerTreeFrameSink.
-      scheduler_->DidSubmitCompositorFrame(frame.frame_token);
+      scheduler_->DidSubmitCompositorFrame(frame.frame_token,
+                                           host_impl_->TakeEventsMetrics());
     }
     result = DRAW_SUCCESS;
   } else {
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index b933c98..5fb3d5c 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -308,6 +308,11 @@
     TRACE_EVENT_INSTANT0("cc", "EarlyOut_NoUpdates", TRACE_EVENT_SCOPE_THREAD);
     std::vector<std::unique_ptr<SwapPromise>> swap_promises =
         layer_tree_host_->GetSwapPromiseManager()->TakeSwapPromises();
+
+    // Since the BeginMainFrame has been aborted, handling of events on the main
+    // frame had no effect and no metrics should be reported for such events.
+    layer_tree_host_->ClearEventsMetrics();
+
     ImplThreadTaskRunner()->PostTask(
         FROM_HERE,
         base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl,
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 1c424d4..3d36a55d 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -695,7 +695,7 @@
         if (scheduler_on_impl_thread_) {
           // Drawing implies we submitted a frame to the LayerTreeFrameSink.
           scheduler_on_impl_thread_->DidSubmitCompositorFrame(
-              frame->frame_token);
+              frame->frame_token, host_impl_->TakeEventsMetrics());
         }
         single_thread_client_->DidSubmitCompositorFrame();
       }