[go: nahoru, domu]

Split `GenerationToRendererCompositor` breakdown to
`GenerationToBrowserMain` and `BrowserMainToRendererCompositor`.


This CL solves problem that there is no way to say what is causing the long
delay -- Browser or Renderer. New breakdowns make it clearer where time was
spent:
 - [KGenerated, kArrivedInBrowserMain] = "GenerationToBrowserMain". This is
 the time before input arrived to the Browser process.
 - [kArrivedInBrowserMain, kArrivedInRendererCompositor] =
 "BrowserMainToRendererCompositor". This is time before input arrived to
 the Render process. Including queuing in Browser.
Currently these breakdowns are added for ScrollBegin and ScrollUpdate for
now, but in next CL it will be added for others EventLatencies. Also we are
going to add a new breakdown for queuing time in browser main.


Result:
 - link: https://ui.perfetto.dev/#!/?s=e2e8ca759182689ab191d1ec67a3e59f44d7e698d754c2e60bad2362fc573b7
 - screenshot: https://screenshot.googleplex.com/8ezhHtVCBmvnzZi

Bug: b/224960731
Change-Id: I703553f886a6c59190b40f34f2972cff9836dbc5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3803196
Reviewed-by: ccameron chromium <ccameron@chromium.org>
Reviewed-by: Steve Kobes <skobes@chromium.org>
Auto-Submit: Violetta Fedotova <violettfaid@google.com>
Commit-Queue: Violetta Fedotova <violettfaid@google.com>
Reviewed-by: Mohsen Izadi <mohsen@chromium.org>
Reviewed-by: Stephen Nusko <nuskos@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1042662}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index ff11c18..356fe96 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -734,6 +734,7 @@
     "metrics/compositor_frame_reporting_controller_unittest.cc",
     "metrics/compositor_timing_history_unittest.cc",
     "metrics/dropped_frame_counter_unittest.cc",
+    "metrics/event_metrics_unittest.cc",
     "metrics/events_metrics_manager_unittest.cc",
     "metrics/frame_info_unittest.cc",
     "metrics/frame_sequence_metrics_unittest.cc",
diff --git a/cc/metrics/average_lag_tracking_manager_unittest.cc b/cc/metrics/average_lag_tracking_manager_unittest.cc
index d313bb8..c6b7c8d 100644
--- a/cc/metrics/average_lag_tracking_manager_unittest.cc
+++ b/cc/metrics/average_lag_tracking_manager_unittest.cc
@@ -58,14 +58,15 @@
     int events_count = gpu_swap_times[0] / scroll_rate;
     EventMetricsSet events;
     base::TimeTicks event_time = MillisecondsToTimeTicks(scroll_rate);
+    base::TimeDelta time_to_rwh = base::Milliseconds(1);
     events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
         ScrollUpdateEventMetrics::ScrollUpdateType::kStarted, event_time,
-        scroll_delta));
+        event_time + time_to_rwh, scroll_delta));
     for (int i = 1; i < events_count; i++) {
       event_time += base::Milliseconds(scroll_rate);
       events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
           ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, event_time,
-          scroll_delta));
+          event_time + time_to_rwh, scroll_delta));
     }
     average_lag_tracking_manager_.CollectScrollEventsFromFrame(0, events);
 
@@ -78,7 +79,7 @@
         event_time += base::Milliseconds(scroll_rate);
         events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
             ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, event_time,
-            scroll_delta));
+            event_time + time_to_rwh, scroll_delta));
       }
       average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame, events);
     }
@@ -88,11 +89,13 @@
   std::unique_ptr<ScrollUpdateEventMetrics> PrepareScrollUpdateEvent(
       ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type,
       base::TimeTicks event_time,
+      base::TimeTicks arrived_in_browser_main_timestamp,
       float delta) {
     const bool kScrollIsNotInertial = false;
     return ScrollUpdateEventMetrics::Create(
         ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kTouchscreen,
-        kScrollIsNotInertial, scroll_update_type, delta, event_time);
+        kScrollIsNotInertial, scroll_update_type, delta, event_time,
+        arrived_in_browser_main_timestamp);
   }
 
   AverageLagTrackingManager average_lag_tracking_manager_;
@@ -108,18 +111,21 @@
   const float scroll_delta = 10.0f;
 
   base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+  base::TimeTicks arrived_in_browser_main_timestamp =
+      MillisecondsToTimeTicks(7);
   base::TimeTicks gpu_swap_time = MillisecondsToTimeTicks(10);
   base::TimeTicks presentation_time = MillisecondsToTimeTicks(13);
   int frame_id = 1;
 
   // ScrollBegin
-  event_time += base::Milliseconds(10);         // 15ms
-  gpu_swap_time += base::Milliseconds(10);      // 20ms
-  presentation_time += base::Milliseconds(10);  // 23ms
+  event_time += base::Milliseconds(10);                         // 15ms
+  arrived_in_browser_main_timestamp += base::Milliseconds(10);  // 17ms
+  gpu_swap_time += base::Milliseconds(10);                      // 20ms
+  presentation_time += base::Milliseconds(10);                  // 23ms
   EventMetricsSet events;
   events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
       ScrollUpdateEventMetrics::ScrollUpdateType::kStarted, event_time,
-      scroll_delta));
+      arrived_in_browser_main_timestamp, scroll_delta));
   average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame_id, events);
   average_lag_tracking_manager_.DidPresentCompositorFrame(
       frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
@@ -129,6 +135,7 @@
   const int kUpdates = 101;
   for (int i = 0; i < kUpdates; i++) {
     event_time += base::Milliseconds(10);
+    arrived_in_browser_main_timestamp += base::Milliseconds(10);
     gpu_swap_time += base::Milliseconds(10);
     presentation_time += base::Milliseconds(10);
     // First 50 has positive delta, others negative delta.
@@ -137,7 +144,7 @@
     events.main_event_metrics.clear();
     events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
         ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, event_time,
-        sign * scroll_delta));
+        arrived_in_browser_main_timestamp, sign * scroll_delta));
     average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame_id,
                                                                events);
     average_lag_tracking_manager_.DidPresentCompositorFrame(
@@ -148,6 +155,7 @@
   // at 1020ms. The last event_time that finish this report should be later than
   // 1020ms.
   EXPECT_EQ(event_time, MillisecondsToTimeTicks(1025));
+  EXPECT_EQ(arrived_in_browser_main_timestamp, MillisecondsToTimeTicks(1027));
   EXPECT_EQ(gpu_swap_time, MillisecondsToTimeTicks(1030));
   EXPECT_EQ(presentation_time, MillisecondsToTimeTicks(1033));
 
@@ -215,6 +223,7 @@
   const float scroll_delta = 100.0f;
 
   std::vector<int> event_times = {500, 1500, 2500, 3500};
+  std::vector<int> arrived_in_browser_main_timestamps = {700, 1700, 2700, 3700};
   std::vector<int> gpu_swap_times = {900, 1900, 2900, 3900};
   std::vector<int> presentation_times = {1000, 2000, 3000, 4000};
 
@@ -224,7 +233,9 @@
   EventMetricsSet events;
   events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
       ScrollUpdateEventMetrics::ScrollUpdateType::kStarted,
-      MillisecondsToTimeTicks(event_times[0]), scroll_delta));
+      MillisecondsToTimeTicks(event_times[0]),
+      MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[0]),
+      scroll_delta));
   average_lag_tracking_manager_.CollectScrollEventsFromFrame(0, events);
   average_lag_tracking_manager_.DidPresentCompositorFrame(
       0, PrepareFrameDetails(MillisecondsToTimeTicks(gpu_swap_times[0]),
@@ -240,7 +251,9 @@
   events.main_event_metrics.clear();
   events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
       ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
-      MillisecondsToTimeTicks(event_times[1]), scroll_delta));
+      MillisecondsToTimeTicks(event_times[1]),
+      MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[1]),
+      scroll_delta));
   average_lag_tracking_manager_.CollectScrollEventsFromFrame(1, events);
   histogram_tester.ExpectTotalCount(
       "Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 0);
@@ -253,7 +266,9 @@
   events.main_event_metrics.clear();
   events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
       ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
-      MillisecondsToTimeTicks(event_times[2]), scroll_delta));
+      MillisecondsToTimeTicks(event_times[2]),
+      MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[2]),
+      scroll_delta));
   average_lag_tracking_manager_.CollectScrollEventsFromFrame(2, events);
   average_lag_tracking_manager_.DidPresentCompositorFrame(
       2, PrepareFailedFrameDetails());
@@ -279,7 +294,9 @@
   // frame 1 should be reported.
   events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
       ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
-      MillisecondsToTimeTicks(event_times[3]), scroll_delta));
+      MillisecondsToTimeTicks(event_times[3]),
+      MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[3]),
+      scroll_delta));
   average_lag_tracking_manager_.CollectScrollEventsFromFrame(3, events);
   average_lag_tracking_manager_.DidPresentCompositorFrame(
       3, PrepareFrameDetails(MillisecondsToTimeTicks(gpu_swap_times[3]),
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc
index 105435c5..c701074 100644
--- a/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -92,10 +92,14 @@
       const std::vector<int>& stage_durations) {
     if (metrics) {
       int num_stages = stage_durations.size();
-      // Start indexing from 1 because the 0th index held duration from
+      int max_num_stages =
+          static_cast<int>(EventMetrics::DispatchStage::kMaxValue) + 1;
+      CHECK(num_stages <= max_num_stages)
+          << num_stages << " > " << max_num_stages;
+      // Start indexing from 2 because the 0th index held duration from
       // kGenerated to kArrivedInRendererCompositor, which was already used in
       // when the EventMetrics was created.
-      for (int i = 1; i < num_stages; i++) {
+      for (int i = 2; i < num_stages; i++) {
         if (stage_durations[i] >= 0) {
           AdvanceNowByUs(stage_durations[i]);
           metrics->SetDispatchStageTimestamp(
@@ -122,34 +126,47 @@
       bool is_inertial,
       ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type,
       const std::vector<int>& stage_durations) {
-    DCHECK_GT((int)stage_durations.size(), 0);
+    CHECK_GE((int)stage_durations.size(), 2);
+
     const base::TimeTicks event_time = AdvanceNowByUs(3);
-    AdvanceNowByUs(stage_durations[0]);
+
+    // kGenerated -> kArrivedInBrowserMain
+    int begin_rwh_latency_ms = stage_durations[0];
+    const base::TimeTicks arrived_in_browser_main_timestamp =
+        AdvanceNowByUs(begin_rwh_latency_ms);
+
+    // kArrivedInBrowserMain -> kArrivedInRendererCompositor
+    AdvanceNowByUs(stage_durations[1]);
+
     // Creates a kGestureScrollUpdate event.
     return SetupEventMetricsWithDispatchTimes(
         ScrollUpdateEventMetrics::CreateForTesting(
             ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kWheel,
             is_inertial, scroll_update_type, /*delta=*/10.0f, event_time,
-            &test_tick_clock_),
+            arrived_in_browser_main_timestamp, &test_tick_clock_),
         stage_durations);
   }
 
   std::unique_ptr<EventMetrics> CreateScrollBeginMetrics() {
     const base::TimeTicks event_time = AdvanceNowByUs(3);
+    const base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByUs(2);
     AdvanceNowByUs(3);
     return SetupEventMetrics(ScrollEventMetrics::CreateForTesting(
         ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kWheel,
-        /*is_inertial=*/false, event_time, &test_tick_clock_));
+        /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp,
+        &test_tick_clock_));
   }
 
   std::unique_ptr<EventMetrics> CreateScrollUpdateEventMetrics(
       bool is_inertial,
       ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type) {
     const base::TimeTicks event_time = AdvanceNowByUs(3);
+    const base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByUs(2);
     AdvanceNowByUs(3);
     return SetupEventMetrics(ScrollUpdateEventMetrics::CreateForTesting(
         ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kWheel, is_inertial,
-        scroll_update_type, /*delta=*/10.0f, event_time, &test_tick_clock_));
+        scroll_update_type, /*delta=*/10.0f, event_time,
+        arrived_in_browser_main_timestamp, &test_tick_clock_));
   }
 
   std::unique_ptr<EventMetrics> CreatePinchEventMetrics(
@@ -1467,7 +1484,13 @@
 // Tests that when a frame is presented to the user, event latency predictions
 // are reported properly.
 TEST_F(CompositorFrameReporterTest, EventLatencyDispatchPredictions) {
-  std::vector<int> dispatch_times = {300, 300, 300, 300, 300};
+  std::vector<int> dispatch_times = {
+      /*[kGenerated, kArrivedInBrowserMain]=*/300,
+      /*[kArrivedInBrowserMain, kArrivedInRendererCompositor]=*/300,
+      /*[kArrivedInRendererCompositor, kRendererCompositorStarted]=*/300,
+      /*[kRendererCompositorStarted, kRendererCompositorFinished]=*/300,
+      /*[kRendererCompositorFinished, kRendererMainStarted]=*/300,
+      /*[kRendererMainStarted, kRendererMainFinished]=*/300};
   std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
       CreateScrollUpdateEventMetricsWithDispatchTimes(
           false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
@@ -1494,9 +1517,14 @@
   std::vector<base::TimeDelta> expected_predictions1(kNumDispatchStages,
                                                      base::Microseconds(-1));
   IntToTimeDeltaVector(expected_predictions1,
-                       std::vector<int>{300, 300, 300, 300, 300});
+                       std::vector<int>{/*kArrivedInBrowserMain=*/300,
+                                        /*kArrivedInRendererCompositor=*/300,
+                                        /*kRendererCompositorStarted=*/300,
+                                        /*kRendererCompositorFinished=*/300,
+                                        /*kRendererMainStarted=*/300,
+                                        /*kRendererMainFinished=*/300});
   base::TimeDelta expected_transition1 = base::Microseconds(300);
-  base::TimeDelta expected_total1 = base::Microseconds(2100);
+  base::TimeDelta expected_total1 = base::Microseconds(2400);
   CompositorFrameReporter::EventLatencyInfo actual_predictions1 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
@@ -1507,14 +1535,14 @@
   std::vector<base::TimeDelta> expected_predictions2(kNumDispatchStages,
                                                      base::Microseconds(-1));
   IntToTimeDeltaVector(expected_predictions2,
-                       std::vector<int>{262, 300, 412, 225, 450});
+                       std::vector<int>{262, 262, 300, 412, 225, 450});
   base::TimeDelta expected_transition2 = base::Microseconds(390);
-  base::TimeDelta expected_total2 = base::Microseconds(2339);
+  base::TimeDelta expected_total2 = base::Microseconds(2601);
   CompositorFrameReporter::EventLatencyInfo actual_predictions2 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions2.dispatch_durations,
-                       std::vector<int>{250, 300, 450, 200, 500});
+                       std::vector<int>{250, 250, 300, 450, 200, 500});
   actual_predictions2.transition_duration = base::Microseconds(420);
   pipeline_reporter_->CalculateEventLatencyPrediction(
       actual_predictions2, kLatencyPredictionDeviationThreshold);
@@ -1523,14 +1551,14 @@
   std::vector<base::TimeDelta> expected_predictions3(kNumDispatchStages,
                                                      base::Microseconds(-1));
   IntToTimeDeltaVector(expected_predictions3,
-                       std::vector<int>{375, 450, 300, 300, 300});
+                       std::vector<int>{300, 375, 450, 300, 300, 300});
   base::TimeDelta expected_transition3 = base::Microseconds(270);
-  base::TimeDelta expected_total3 = base::Microseconds(2295);
+  base::TimeDelta expected_total3 = base::Microseconds(2595);
   CompositorFrameReporter::EventLatencyInfo actual_predictions3 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions3.dispatch_durations,
-                       std::vector<int>{400, 500, 300, -1, -1});
+                       std::vector<int>{-1, 400, 500, 300, -1, -1});
   actual_predictions3.transition_duration = base::Microseconds(260);
   pipeline_reporter_->CalculateEventLatencyPrediction(
       actual_predictions3, kLatencyPredictionDeviationThreshold);
@@ -1557,9 +1585,15 @@
 // the user, event latency predictions are reported properly.
 TEST_F(CompositorFrameReporterTest,
        EventLatencyDispatchPredictionsWithMissingStages) {
-  // Invalid EventLatency stage durations will cause program to crash,
-  // validity checked in event_latency_tracing_recorder.cc.
-  std::vector<int> dispatch_times = {400, 600, 700, -1, -1};
+  // Invalid EventLatency stage durations will cause program to crash, validity
+  // checked in event_latency_tracing_recorder.cc.
+  std::vector<int> dispatch_times = {
+      /*[kGenerated, kArrivedInBrowserMain]=*/200,
+      /*[kArrivedInBrowserMain, kArrivedInRendererCompositor]=*/400,
+      /*[kArrivedInRendererCompositor, kRendererCompositorStarted]=*/600,
+      /*[kRendererCompositorStarted, kRendererCompositorFinished]=*/700,
+      /*[kRendererCompositorFinished, kRendererMainStarted]=*/-1,
+      /*[kRendererMainStarted, kRendererMainFinished]=*/-1};
   std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
       CreateScrollUpdateEventMetricsWithDispatchTimes(
           false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
@@ -1586,9 +1620,9 @@
   std::vector<base::TimeDelta> expected_predictions1(kNumDispatchStages,
                                                      base::Microseconds(-1));
   IntToTimeDeltaVector(expected_predictions1,
-                       std::vector<int>{400, 600, 700, -1, -1});
+                       std::vector<int>{200, 400, 600, 700, -1, -1});
   base::TimeDelta expected_transition1 = base::Microseconds(470);
-  base::TimeDelta expected_total1 = base::Microseconds(2470);
+  base::TimeDelta expected_total1 = base::Microseconds(2670);
   CompositorFrameReporter::EventLatencyInfo actual_predictions1 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
@@ -1599,14 +1633,14 @@
   std::vector<base::TimeDelta> expected_predictions2(kNumDispatchStages,
                                                      base::Microseconds(-1));
   IntToTimeDeltaVector(expected_predictions2,
-                       std::vector<int>{250, 375, 475, 200, 500});
+                       std::vector<int>{125, 250, 375, 475, 200, 500});
   base::TimeDelta expected_transition2 = base::Microseconds(402);
-  base::TimeDelta expected_total2 = base::Microseconds(2502);
+  base::TimeDelta expected_total2 = base::Microseconds(2627);
   CompositorFrameReporter::EventLatencyInfo actual_predictions2 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions2.dispatch_durations,
-                       std::vector<int>{200, 300, 400, 200, 500});
+                       std::vector<int>{100, 200, 300, 400, 200, 500});
   actual_predictions2.transition_duration = base::Microseconds(380);
   pipeline_reporter_->CalculateEventLatencyPrediction(
       actual_predictions2, kLatencyPredictionDeviationThreshold);
@@ -1615,14 +1649,14 @@
   std::vector<base::TimeDelta> expected_predictions3(kNumDispatchStages,
                                                      base::Microseconds(-1));
   IntToTimeDeltaVector(expected_predictions3,
-                       std::vector<int>{400, 525, 745, -1, -1});
+                       std::vector<int>{143, 400, 525, 745, -1, -1});
   base::TimeDelta expected_transition3 = base::Microseconds(492);
-  base::TimeDelta expected_total3 = base::Microseconds(2462);
+  base::TimeDelta expected_total3 = base::Microseconds(2605);
   CompositorFrameReporter::EventLatencyInfo actual_predictions3 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions3.dispatch_durations,
-                       std::vector<int>{400, 500, 760, -1, -1});
+                       std::vector<int>{125, 400, 500, 760, -1, -1});
   actual_predictions3.transition_duration = base::Microseconds(500);
   pipeline_reporter_->CalculateEventLatencyPrediction(
       actual_predictions3, kLatencyPredictionDeviationThreshold);
@@ -1648,7 +1682,14 @@
 // Tests that when a frame is presented to the user, event latency predictions
 // are reported properly.
 TEST_F(CompositorFrameReporterTest, EventLatencyCompositorPredictions) {
-  std::vector<int> dispatch_times = {300, 300, 300, 300, 300};
+  std::vector<int> dispatch_times = {
+      /*[kGenerated, kArrivedInBrowserMain]=*/300,
+      /*[kArrivedInBrowserMain, kArrivedInRendererCompositor]=*/300,
+      /*[kArrivedInRendererCompositor, kRendererCompositorStarted]=*/300,
+      /*[kRendererCompositorStarted, kRendererCompositorFinished]=*/300,
+      /*[kRendererCompositorFinished, kRendererMainStarted]=*/300,
+      /*[kRendererMainStarted, kRendererMainFinished]=*/300};
+
   std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
       CreateScrollUpdateEventMetricsWithDispatchTimes(
           false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
@@ -1688,13 +1729,18 @@
   std::vector<base::TimeDelta> expected_dispatch1(kNumDispatchStages,
                                                   base::Microseconds(-1));
   IntToTimeDeltaVector(expected_dispatch1,
-                       std::vector<int>{300, 300, 300, 300, 300});
+                       std::vector<int>{/*kArrivedInBrowserMain=*/300,
+                                        /*kArrivedInRendererCompositor=*/300,
+                                        /*kRendererCompositorStarted=*/300,
+                                        /*kRendererCompositorFinished=*/300,
+                                        /*kRendererMainStarted=*/300,
+                                        /*kRendererMainFinished=*/300});
   base::TimeDelta expected_transition1 = base::Microseconds(300);
   std::vector<base::TimeDelta> expected_compositor1(kNumOfCompositorStages,
                                                     base::Microseconds(-1));
   IntToTimeDeltaVector(expected_compositor1,
                        std::vector<int>{300, -1, -1, -1, -1, 300, 300});
-  base::TimeDelta expected_total1 = base::Microseconds(2700);
+  base::TimeDelta expected_total1 = base::Microseconds(3000);
   CompositorFrameReporter::EventLatencyInfo actual_predictions1 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
@@ -1705,18 +1751,18 @@
   std::vector<base::TimeDelta> expected_dispatch2(kNumDispatchStages,
                                                   base::Microseconds(-1));
   IntToTimeDeltaVector(expected_dispatch2,
-                       std::vector<int>{262, 300, 412, 225, 450});
+                       std::vector<int>{262, 262, 300, 412, 225, 450});
   base::TimeDelta expected_transition2 = base::Microseconds(390);
   std::vector<base::TimeDelta> expected_compositor2(kNumOfCompositorStages,
                                                     base::Microseconds(-1));
   IntToTimeDeltaVector(expected_compositor2,
                        std::vector<int>{465, 500, 90, 720, 410, 742, 390});
-  base::TimeDelta expected_total2 = base::Microseconds(5356);
+  base::TimeDelta expected_total2 = base::Microseconds(5618);
   CompositorFrameReporter::EventLatencyInfo actual_predictions2 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions2.dispatch_durations,
-                       std::vector<int>{250, 300, 450, 200, 500});
+                       std::vector<int>{250, 250, 300, 450, 200, 500});
   actual_predictions2.transition_duration = base::Microseconds(420);
   IntToTimeDeltaVector(actual_predictions2.compositor_durations,
                        std::vector<int>{520, 500, 90, 720, 410, 890, 420});
@@ -1727,18 +1773,18 @@
   std::vector<base::TimeDelta> expected_dispatch3(kNumDispatchStages,
                                                   base::Microseconds(-1));
   IntToTimeDeltaVector(expected_dispatch3,
-                       std::vector<int>{375, 450, 300, 300, 300});
+                       std::vector<int>{375, 375, 450, 300, 300, 300});
   base::TimeDelta expected_transition3 = base::Microseconds(270);
   std::vector<base::TimeDelta> expected_compositor3(kNumOfCompositorStages,
                                                     base::Microseconds(-1));
   IntToTimeDeltaVector(expected_compositor3,
                        std::vector<int>{300, 500, -1, -1, 410, 742, 390});
-  base::TimeDelta expected_total3 = base::Microseconds(4337);
+  base::TimeDelta expected_total3 = base::Microseconds(4712);
   CompositorFrameReporter::EventLatencyInfo actual_predictions3 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions3.dispatch_durations,
-                       std::vector<int>{400, 500, 300, -1, -1});
+                       std::vector<int>{400, 400, 500, 300, -1, -1});
   actual_predictions3.transition_duration = base::Microseconds(260);
   IntToTimeDeltaVector(actual_predictions3.compositor_durations,
                        std::vector<int>{-1, 500, -1, -1, 410, 890, 420});
@@ -1771,7 +1817,13 @@
 // Tests that when a frame is presented to the user, event latency predictions
 // are reported properly for filtered EventTypes.
 TEST_F(CompositorFrameReporterTest, EventLatencyMultipleEventTypePredictions) {
-  std::vector<int> dispatch_times = {300, 300, 300, 300, 300};
+  std::vector<int> dispatch_times = {
+      /*[kGenerated, kArrivedInBrowserMain]=*/300,
+      /*[kArrivedInBrowserMain, kArrivedInRendererCompositor]=*/300,
+      /*[kArrivedInRendererCompositor, kRendererCompositorStarted]=*/300,
+      /*[kRendererCompositorStarted, kRendererCompositorFinished]=*/300,
+      /*[kRendererCompositorFinished, kRendererMainStarted]=*/300,
+      /*[kRendererMainStarted, kRendererMainFinished]=*/300};
   // The prediction should only be updated with the ScrollUpdateType event,
   // other EventType metrics should be ignored.
   std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
@@ -1820,7 +1872,12 @@
   std::vector<base::TimeDelta> expected_dispatch1(kNumDispatchStages,
                                                   base::Microseconds(-1));
   IntToTimeDeltaVector(expected_dispatch1,
-                       std::vector<int>{300, 300, 300, 300, 300});
+                       std::vector<int>{/*kArrivedInBrowserMain=*/300,
+                                        /*kArrivedInRendererCompositor=*/300,
+                                        /*kRendererCompositorStarted=*/300,
+                                        /*kRendererCompositorFinished=*/300,
+                                        /*kRendererMainStarted=*/300,
+                                        /*kRendererMainFinished=*/300});
   base::TimeDelta expected_transition1 =
       base::Microseconds(300) + kTouchEventTransition;
   std::vector<base::TimeDelta> expected_compositor1(kNumOfCompositorStages,
@@ -1828,7 +1885,7 @@
   IntToTimeDeltaVector(expected_compositor1,
                        std::vector<int>{300, -1, -1, -1, -1, 300, 300});
   base::TimeDelta expected_total1 =
-      base::Microseconds(2700) + kTouchEventTransition;
+      base::Microseconds(3000) + kTouchEventTransition;
   CompositorFrameReporter::EventLatencyInfo actual_predictions1 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
@@ -1839,18 +1896,18 @@
   std::vector<base::TimeDelta> expected_dispatch2(kNumDispatchStages,
                                                   base::Microseconds(-1));
   IntToTimeDeltaVector(expected_dispatch2,
-                       std::vector<int>{262, 300, 412, 225, 450});
+                       std::vector<int>{262, 262, 300, 412, 225, 450});
   base::TimeDelta expected_transition2 = base::Microseconds(393);
   std::vector<base::TimeDelta> expected_compositor2(kNumOfCompositorStages,
                                                     base::Microseconds(-1));
   IntToTimeDeltaVector(expected_compositor2,
                        std::vector<int>{465, 500, 90, 720, 410, 742, 390});
-  base::TimeDelta expected_total2 = base::Microseconds(5359);
+  base::TimeDelta expected_total2 = base::Microseconds(5621);
   CompositorFrameReporter::EventLatencyInfo actual_predictions2 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions2.dispatch_durations,
-                       std::vector<int>{250, 300, 450, 200, 500});
+                       std::vector<int>{250, 250, 300, 450, 200, 500});
   actual_predictions2.transition_duration = base::Microseconds(420);
   IntToTimeDeltaVector(actual_predictions2.compositor_durations,
                        std::vector<int>{520, 500, 90, 720, 410, 890, 420});
@@ -1878,7 +1935,13 @@
 // Tests that when a frame is presented to the user, high latency attribution
 // for EventLatency is reported properly for filtered EventTypes.
 TEST_F(CompositorFrameReporterTest, EventLatencyAttributionPredictions) {
-  std::vector<int> dispatch_times = {300, 300, 300, 50000, 300};
+  std::vector<int> dispatch_times = {
+      /*[kGenerated, kArrivedInBrowserMain]=*/300,
+      /*[kArrivedInBrowserMain, kArrivedInRendererCompositor]=*/300,
+      /*[kArrivedInRendererCompositor, kRendererCompositorStarted]=*/300,
+      /*[kRendererCompositorStarted, kRendererCompositorFinished]=*/300,
+      /*[kRendererCompositorFinished, kRendererMainStarted]=*/50000,
+      /*[kRendererMainStarted, kRendererMainFinished]=*/300};
   // The prediction should only be updated with the ScrollUpdateType event,
   // other EventType metrics should be ignored.
   std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
@@ -1921,11 +1984,11 @@
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(expected_predictions1.dispatch_durations,
-                       std::vector<int>{300, 300, 300, 50000, 300});
+                       std::vector<int>{300, 300, 300, 300, 50000, 300});
   expected_predictions1.transition_duration = base::Microseconds(300);
   IntToTimeDeltaVector(expected_predictions1.compositor_durations,
                        std::vector<int>{300, -1, -1, -1, -1, 50000, 300});
-  expected_predictions1.total_duration = base::Microseconds(102100);
+  expected_predictions1.total_duration = base::Microseconds(102400);
 
   CompositorFrameReporter::EventLatencyInfo actual_predictions1 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
@@ -1944,21 +2007,21 @@
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(expected_predictions2.dispatch_durations,
-                       std::vector<int>{300, 300, 300, 12725, 300});
+                       std::vector<int>{300, 300, 300, 300, 12725, 300});
   expected_predictions2.transition_duration = base::Microseconds(300);
   IntToTimeDeltaVector(expected_predictions2.compositor_durations,
                        std::vector<int>{300, -1, -1, -1, -1, 50000, 300});
-  expected_predictions2.total_duration = base::Microseconds(64825);
+  expected_predictions2.total_duration = base::Microseconds(65125);
 
   CompositorFrameReporter::EventLatencyInfo actual_predictions2 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions2.dispatch_durations,
-                       std::vector<int>{300, 300, 300, 300, 300});
+                       std::vector<int>{300, 300, 300, 300, 300, 300});
   actual_predictions2.transition_duration = base::Microseconds(300);
   IntToTimeDeltaVector(actual_predictions2.compositor_durations,
                        std::vector<int>{300, -1, -1, -1, -1, 50000, 300});
-  actual_predictions2.total_duration = base::Microseconds(52400);
+  actual_predictions2.total_duration = base::Microseconds(5200);
   pipeline_reporter_->CalculateEventLatencyPrediction(
       actual_predictions2, kLatencyPredictionDeviationThreshold);
 
@@ -1972,17 +2035,17 @@
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(expected_predictions3.dispatch_durations,
-                       std::vector<int>{300, 300, 300, 12725, 300});
+                       std::vector<int>{300, 300, 300, 300, 12725, 300});
   expected_predictions3.transition_duration = base::Microseconds(300);
   IntToTimeDeltaVector(expected_predictions3.compositor_durations,
                        std::vector<int>{300, -1, -1, -1, -1, 12725, 300});
-  expected_predictions3.total_duration = base::Microseconds(27550);
+  expected_predictions3.total_duration = base::Microseconds(27850);
 
   CompositorFrameReporter::EventLatencyInfo actual_predictions3 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions3.dispatch_durations,
-                       std::vector<int>{300, 300, 300, 300, 300});
+                       std::vector<int>{300, 300, 300, 300, 300, 300});
   actual_predictions3.transition_duration = base::Microseconds(300);
   IntToTimeDeltaVector(actual_predictions3.compositor_durations,
                        std::vector<int>{300, -1, -1, -1, -1, 300, 300});
@@ -2001,16 +2064,21 @@
               actual_predictions1.dispatch_durations[i]);
     EXPECT_EQ(expected_predictions2.dispatch_durations[i],
               actual_predictions2.dispatch_durations[i]);
+    ;
     EXPECT_EQ(expected_predictions3.dispatch_durations[i],
               actual_predictions3.dispatch_durations[i]);
+    ;
   }
   for (int i = 0; i < kNumOfCompositorStages; i++) {
     EXPECT_EQ(expected_predictions1.compositor_durations[i],
               actual_predictions1.compositor_durations[i]);
+    ;
     EXPECT_EQ(expected_predictions2.compositor_durations[i],
               actual_predictions2.compositor_durations[i]);
+    ;
     EXPECT_EQ(expected_predictions3.compositor_durations[i],
               actual_predictions3.compositor_durations[i]);
+    ;
   }
   EXPECT_EQ(expected_predictions1.transition_duration,
             actual_predictions1.transition_duration);
@@ -2031,7 +2099,14 @@
 // Tests that when a frame is presented to the user, high latency attribution
 // for EventLatency is reported properly for filtered EventTypes.
 TEST_F(CompositorFrameReporterTest, EventLatencyAttributionChangePredictions) {
-  std::vector<int> dispatch_times = {40000, -1, -1, 300, 50000};
+  std::vector<int> dispatch_times = {
+      /*[kGenerated, kArrivedInBrowserMain]=*/40000,
+      /*[kArrivedInBrowserMain, kArrivedInRendererCompositor]=*/150,
+      /*[kArrivedInRendererCompositor, kRendererCompositorStarted]=*/-1,
+      /*[kRendererCompositorStarted, kRendererCompositorFinished]=*/-1,
+      /*[kRendererCompositorFinished, kRendererMainStarted]=*/150,
+      /*[kRendererMainStarted, kRendererMainFinished]=*/50000};
+
   // The prediction should only be updated with the ScrollUpdateType event,
   // other EventType metrics should be ignored.
   std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
@@ -2074,29 +2149,29 @@
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(expected_predictions1.dispatch_durations,
-                       std::vector<int>{10300, -1, -1, 300, 42500});
+                       std::vector<int>{10300, 262, -1, -1, 262, 42500});
   expected_predictions1.transition_duration = base::Microseconds(300);
   IntToTimeDeltaVector(expected_predictions1.compositor_durations,
                        std::vector<int>{300, -1, -1, -1, -1, 15200, 300});
-  expected_predictions1.total_duration = base::Microseconds(69200);
+  expected_predictions1.total_duration = base::Microseconds(69424);
 
   CompositorFrameReporter::EventLatencyInfo actual_predictions1 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions1.dispatch_durations,
-                       std::vector<int>{400, -1, -1, 300, 40000});
+                       std::vector<int>{400, 300, -1, -1, 300, 40000});
   actual_predictions1.transition_duration = base::Microseconds(300);
   IntToTimeDeltaVector(actual_predictions1.compositor_durations,
                        std::vector<int>{300, -1, -1, -1, -1, 3600, 300});
-  actual_predictions1.total_duration = base::Microseconds(45200);
+  actual_predictions1.total_duration = base::Microseconds(45500);
   pipeline_reporter_->CalculateEventLatencyPrediction(
       actual_predictions1, kLatencyPredictionDeviationThreshold);
 
   std::unique_ptr<EventMetrics>& event_metrics =
       pipeline_reporter_->events_metrics_for_testing()[0];
   std::vector<std::string> attribution = event_metrics->GetHighLatencyStages();
-  EXPECT_EQ(2, (int)attribution.size());
-  EXPECT_EQ("GenerationToRendererCompositor", attribution[0]);
+  EXPECT_EQ(2, static_cast<int>(attribution.size()));
+  EXPECT_EQ("GenerationToBrowserMain", attribution[0]);
   EXPECT_EQ("EndActivateToSubmitCompositorFrame", attribution[1]);
   event_metrics->ClearHighLatencyStagesForTesting();
 
@@ -2105,18 +2180,18 @@
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(expected_predictions2.dispatch_durations,
-                       std::vector<int>{10225, -1, -1, 300, 12725});
+                       std::vector<int>{10225, 262, -1, -1, 262, 12725});
   expected_predictions2.transition_duration = base::Microseconds(300);
 
   IntToTimeDeltaVector(expected_predictions2.compositor_durations,
                        std::vector<int>{300, -1, -1, -1, -1, 12725, 300});
-  expected_predictions2.total_duration = base::Microseconds(36875);
+  expected_predictions2.total_duration = base::Microseconds(37099);
 
   CompositorFrameReporter::EventLatencyInfo actual_predictions2 =
       CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
                                                 kNumOfCompositorStages);
   IntToTimeDeltaVector(actual_predictions2.dispatch_durations,
-                       std::vector<int>{300, -1, -1, 300, 300});
+                       std::vector<int>{300, 300, -1, -1, 300, 300});
   actual_predictions2.transition_duration = base::Microseconds(300);
   IntToTimeDeltaVector(actual_predictions2.compositor_durations,
                        std::vector<int>{300, -1, -1, -1, -1, 300, 300});
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index c831d79..90a83241 100644
--- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -260,20 +260,24 @@
 
   std::unique_ptr<EventMetrics> CreateScrollBeginEventMetrics() {
     const base::TimeTicks event_time = AdvanceNowByMs(10);
+    const base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByMs(3);
     AdvanceNowByMs(10);
     return SetupEventMetrics(ScrollEventMetrics::CreateForTesting(
         ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kWheel,
-        /*is_inertial=*/false, event_time, &test_tick_clock_));
+        /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp,
+        &test_tick_clock_));
   }
 
   std::unique_ptr<EventMetrics> CreateScrollUpdateEventMetrics(
       bool is_inertial,
       ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type) {
     const base::TimeTicks event_time = AdvanceNowByMs(10);
+    const base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByMs(3);
     AdvanceNowByMs(10);
     return SetupEventMetrics(ScrollUpdateEventMetrics::CreateForTesting(
         ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kWheel, is_inertial,
-        scroll_update_type, /*delta=*/10.0f, event_time, &test_tick_clock_));
+        scroll_update_type, /*delta=*/10.0f, event_time,
+        arrived_in_browser_main_timestamp, &test_tick_clock_));
   }
 
   std::unique_ptr<EventMetrics> CreatePinchEventMetrics(
diff --git a/cc/metrics/event_latency_tracing_recorder.cc b/cc/metrics/event_latency_tracing_recorder.cc
index 138dac53..78ed622 100644
--- a/cc/metrics/event_latency_tracing_recorder.cc
+++ b/cc/metrics/event_latency_tracing_recorder.cc
@@ -61,9 +61,19 @@
     EventMetrics::DispatchStage end_stage) {
   switch (start_stage) {
     case EventMetrics::DispatchStage::kGenerated:
+      switch (end_stage) {
+        case EventMetrics::DispatchStage::kArrivedInBrowserMain:
+          return "GenerationToBrowserMain";
+        case EventMetrics::DispatchStage::kArrivedInRendererCompositor:
+          return "GenerationToRendererCompositor";
+        default:
+          NOTREACHED();
+          return "";
+      }
+    case EventMetrics::DispatchStage::kArrivedInBrowserMain:
       DCHECK_EQ(end_stage,
                 EventMetrics::DispatchStage::kArrivedInRendererCompositor);
-      return "GenerationToRendererCompositor";
+      return "BrowserMainToRendererCompositor";
     case EventMetrics::DispatchStage::kArrivedInRendererCompositor:
       switch (end_stage) {
         case EventMetrics::DispatchStage::kRendererCompositorStarted:
diff --git a/cc/metrics/event_metrics.cc b/cc/metrics/event_metrics.cc
index 62306ee..46443e1 100644
--- a/cc/metrics/event_metrics.cc
+++ b/cc/metrics/event_metrics.cc
@@ -255,6 +255,16 @@
       timestamp;
 }
 
+EventMetrics::EventMetrics(EventType type,
+                           base::TimeTicks timestamp,
+                           base::TimeTicks arrived_in_browser_main_timestamp,
+                           const base::TickClock* tick_clock)
+    : EventMetrics(type, timestamp, tick_clock) {
+  dispatch_stage_timestamps_[static_cast<int>(
+      DispatchStage::kArrivedInBrowserMain)] =
+      arrived_in_browser_main_timestamp;
+}
+
 EventMetrics::EventMetrics(const EventMetrics& other)
     : type_(other.type_),
       tick_clock_(other.tick_clock_),
@@ -345,7 +355,8 @@
     ui::EventType type,
     ui::ScrollInputType input_type,
     bool is_inertial,
-    base::TimeTicks timestamp) {
+    base::TimeTicks timestamp,
+    base::TimeTicks arrived_in_browser_main_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
@@ -353,9 +364,9 @@
 
   DCHECK(IsGestureScroll(type) && !IsGestureScrollUpdate(type));
 
-  std::unique_ptr<ScrollEventMetrics> metrics =
-      CreateInternal(type, input_type, is_inertial, timestamp,
-                     base::DefaultTickClock::GetInstance());
+  std::unique_ptr<ScrollEventMetrics> metrics = CreateInternal(
+      type, input_type, is_inertial, timestamp,
+      arrived_in_browser_main_timestamp, base::DefaultTickClock::GetInstance());
   if (!metrics)
     return nullptr;
 
@@ -365,16 +376,27 @@
 }
 
 // static
+std::unique_ptr<ScrollEventMetrics> ScrollEventMetrics::CreateForBrowser(
+    ui::EventType type,
+    ui::ScrollInputType input_type,
+    bool is_inertial,
+    base::TimeTicks timestamp) {
+  return Create(type, input_type, is_inertial, timestamp, base::TimeTicks());
+}
+
+// static
 std::unique_ptr<ScrollEventMetrics> ScrollEventMetrics::CreateForTesting(
     ui::EventType type,
     ui::ScrollInputType input_type,
     bool is_inertial,
     base::TimeTicks timestamp,
+    base::TimeTicks arrived_in_browser_main_timestamp,
     const base::TickClock* tick_clock) {
   DCHECK(!timestamp.is_null());
 
   std::unique_ptr<ScrollEventMetrics> metrics =
-      CreateInternal(type, input_type, is_inertial, timestamp, tick_clock);
+      CreateInternal(type, input_type, is_inertial, timestamp,
+                     arrived_in_browser_main_timestamp, tick_clock);
   if (!metrics)
     return nullptr;
 
@@ -398,8 +420,9 @@
   if (!existing)
     return nullptr;
 
-  std::unique_ptr<ScrollEventMetrics> metrics = CreateInternal(
-      type, input_type, is_inertial, base::TimeTicks(), existing->tick_clock_);
+  std::unique_ptr<ScrollEventMetrics> metrics =
+      CreateInternal(type, input_type, is_inertial, base::TimeTicks(),
+                     base::TimeTicks(), existing->tick_clock_);
   if (!metrics)
     return nullptr;
 
@@ -416,6 +439,7 @@
     ui::ScrollInputType input_type,
     bool is_inertial,
     base::TimeTicks timestamp,
+    base::TimeTicks arrived_in_browser_main_timestamp,
     const base::TickClock* tick_clock) {
   absl::optional<EventType> interesting_type =
       ToInterestingEventType(type, is_inertial,
@@ -423,14 +447,21 @@
   if (!interesting_type)
     return nullptr;
   return base::WrapUnique(new ScrollEventMetrics(
-      *interesting_type, ToScrollType(input_type), timestamp, tick_clock));
+      *interesting_type, ToScrollType(input_type), timestamp,
+      arrived_in_browser_main_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(
+    EventType type,
+    ScrollType scroll_type,
+    base::TimeTicks timestamp,
+    base::TimeTicks arrived_in_browser_main_timestamp,
+    const base::TickClock* tick_clock)
+    : EventMetrics(type,
+                   timestamp,
+                   arrived_in_browser_main_timestamp,
+                   tick_clock),
+      scroll_type_(scroll_type) {}
 
 ScrollEventMetrics::ScrollEventMetrics(const ScrollEventMetrics&) = default;
 
@@ -462,7 +493,8 @@
     bool is_inertial,
     ScrollUpdateType scroll_update_type,
     float delta,
-    base::TimeTicks timestamp) {
+    base::TimeTicks timestamp,
+    base::TimeTicks arrived_in_browser_main_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
@@ -470,9 +502,9 @@
 
   DCHECK(IsGestureScrollUpdate(type));
 
-  std::unique_ptr<ScrollUpdateEventMetrics> metrics =
-      CreateInternal(type, input_type, is_inertial, scroll_update_type, delta,
-                     timestamp, base::DefaultTickClock::GetInstance());
+  std::unique_ptr<ScrollUpdateEventMetrics> metrics = CreateInternal(
+      type, input_type, is_inertial, scroll_update_type, delta, timestamp,
+      arrived_in_browser_main_timestamp, base::DefaultTickClock::GetInstance());
   if (!metrics)
     return nullptr;
 
@@ -483,18 +515,32 @@
 
 // static
 std::unique_ptr<ScrollUpdateEventMetrics>
-ScrollUpdateEventMetrics::CreateForTesting(ui::EventType type,
+ScrollUpdateEventMetrics::CreateForBrowser(ui::EventType type,
                                            ui::ScrollInputType input_type,
                                            bool is_inertial,
                                            ScrollUpdateType scroll_update_type,
                                            float delta,
-                                           base::TimeTicks timestamp,
-                                           const base::TickClock* tick_clock) {
+                                           base::TimeTicks timestamp) {
+  return Create(type, input_type, is_inertial, scroll_update_type, delta,
+                timestamp, base::TimeTicks());
+}
+
+// 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,
+    base::TimeTicks arrived_in_browser_main_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);
+                     timestamp, arrived_in_browser_main_timestamp, tick_clock);
   if (!metrics)
     return nullptr;
 
@@ -521,9 +567,9 @@
   if (!existing)
     return nullptr;
 
-  std::unique_ptr<ScrollUpdateEventMetrics> metrics =
-      CreateInternal(type, input_type, is_inertial, scroll_update_type, delta,
-                     base::TimeTicks(), existing->tick_clock_);
+  std::unique_ptr<ScrollUpdateEventMetrics> metrics = CreateInternal(
+      type, input_type, is_inertial, scroll_update_type, delta,
+      base::TimeTicks(), base::TimeTicks(), existing->tick_clock_);
   if (!metrics)
     return nullptr;
 
@@ -536,20 +582,22 @@
 
 // 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) {
+ScrollUpdateEventMetrics::CreateInternal(
+    ui::EventType type,
+    ui::ScrollInputType input_type,
+    bool is_inertial,
+    ScrollUpdateType scroll_update_type,
+    float delta,
+    base::TimeTicks timestamp,
+    base::TimeTicks arrived_in_browser_main_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));
+      timestamp, arrived_in_browser_main_timestamp, tick_clock));
 }
 
 ScrollUpdateEventMetrics::ScrollUpdateEventMetrics(
@@ -558,8 +606,13 @@
     ScrollUpdateType scroll_update_type,
     float delta,
     base::TimeTicks timestamp,
+    base::TimeTicks arrived_in_browser_main_timestamp,
     const base::TickClock* tick_clock)
-    : ScrollEventMetrics(type, scroll_type, timestamp, tick_clock),
+    : ScrollEventMetrics(type,
+                         scroll_type,
+                         timestamp,
+                         arrived_in_browser_main_timestamp,
+                         tick_clock),
       delta_(delta),
       predicted_delta_(delta),
       last_timestamp_(timestamp) {}
diff --git a/cc/metrics/event_metrics.h b/cc/metrics/event_metrics.h
index ce133874..98de0ca 100644
--- a/cc/metrics/event_metrics.h
+++ b/cc/metrics/event_metrics.h
@@ -66,6 +66,7 @@
   // Stages of event dispatch in different processes/threads.
   enum class DispatchStage {
     kGenerated,
+    kArrivedInBrowserMain,
     kArrivedInRendererCompositor,
     kRendererCompositorStarted,
     kRendererCompositorFinished,
@@ -152,6 +153,11 @@
                base::TimeTicks timestamp,
                const base::TickClock* tick_clock);
 
+  EventMetrics(EventType type,
+               base::TimeTicks timestamp,
+               base::TimeTicks arrived_in_browser_main_timestamp,
+               const base::TickClock* tick_clock);
+
   // Creates a clone of `other` that might be used in creating `EventMetrics`
   // objects for some injected events. Since this object itself does not
   // directly correspond to an event, it won't be used in recording trace
@@ -213,10 +219,23 @@
   // Returns a new instance if the event is of a type we are interested in.
   // Otherwise, returns `nullptr`. Should only be used for scroll events other
   // than scroll-update.
+  //
+  // TODO(b/224960731): Fix tests and stop supporting the case when
+  // `arrived_in_browser_main_timestamp` is null.
   static std::unique_ptr<ScrollEventMetrics> Create(
       ui::EventType type,
       ui::ScrollInputType input_type,
       bool is_inertial,
+      base::TimeTicks timestamp,
+      base::TimeTicks arrived_in_browser_main_timestamp);
+
+  // Prefer to use `Create()` above. This method is used only by the Browser
+  // process which have own breakdowns.
+  // Similar to `Create()` above but doesn't set kArrivedInBrowserMain.
+  static std::unique_ptr<ScrollEventMetrics> CreateForBrowser(
+      ui::EventType type,
+      ui::ScrollInputType input_type,
+      bool is_inertial,
       base::TimeTicks timestamp);
 
   // Similar to `Create()` with an extra `base::TickClock` to use in tests.
@@ -226,6 +245,7 @@
       ui::ScrollInputType input_type,
       bool is_inertial,
       base::TimeTicks timestamp,
+      base::TimeTicks arrived_in_browser_main_timestamp,
       const base::TickClock* tick_clock);
 
   // Used to create an instance for an event generated based on an existing
@@ -257,6 +277,7 @@
   ScrollEventMetrics(EventType type,
                      ScrollType scroll_type,
                      base::TimeTicks timestamp,
+                     base::TimeTicks arrived_in_browser_main_timestamp,
                      const base::TickClock* tick_clock);
   ScrollEventMetrics(const ScrollEventMetrics&);
 
@@ -266,6 +287,7 @@
       ui::ScrollInputType input_type,
       bool is_inertial,
       base::TimeTicks timestamp,
+      base::TimeTicks arrived_in_browser_main_timestamp,
       const base::TickClock* tick_clock);
 
   // Type of the input device for the event.
@@ -284,12 +306,27 @@
 
   // Returns a new instance if the event is of a type we are interested in.
   // Otherwise, returns `nullptr`. Should only be used for scroll-update events.
+  //
+  // TODO(b/224960731): Fix tests and stop supporting the case when
+  // `arrived_in_browser_main_timestamp` is null.
   static std::unique_ptr<ScrollUpdateEventMetrics> Create(
       ui::EventType type,
       ui::ScrollInputType input_type,
       bool is_inertial,
       ScrollUpdateType scroll_update_type,
       float delta,
+      base::TimeTicks timestamp,
+      base::TimeTicks arrived_in_browser_main_timestamp);
+
+  // Prefer to use `Create()` above. This method is used only by the Browser
+  // process which have own breakdowns.
+  // Similar to `Create()` above but doesn't set kArrivedInBrowserMain.
+  static std::unique_ptr<ScrollUpdateEventMetrics> CreateForBrowser(
+      ui::EventType type,
+      ui::ScrollInputType input_type,
+      bool is_inertial,
+      ScrollUpdateType scroll_update_type,
+      float delta,
       base::TimeTicks timestamp);
 
   // Similar to `Create()` with an extra `base::TickClock` to use in tests.
@@ -301,6 +338,7 @@
       ScrollUpdateType scroll_update_type,
       float delta,
       base::TimeTicks timestamp,
+      base::TimeTicks arrived_in_browser_main_timestamp,
       const base::TickClock* tick_clock);
 
   // Used to create an instance for an event generated based on an existing
@@ -342,6 +380,7 @@
                            ScrollUpdateType scroll_update_type,
                            float delta,
                            base::TimeTicks timestamp,
+                           base::TimeTicks arrived_in_browser_main_timestamp,
                            const base::TickClock* tick_clock);
   ScrollUpdateEventMetrics(const ScrollUpdateEventMetrics&);
 
@@ -353,6 +392,7 @@
       ScrollUpdateType scroll_update_type,
       float delta,
       base::TimeTicks timestamp,
+      base::TimeTicks arrived_in_browser_main_timestamp,
       const base::TickClock* tick_clock);
 
   float delta_;
diff --git a/cc/metrics/event_metrics_unittest.cc b/cc/metrics/event_metrics_unittest.cc
new file mode 100644
index 0000000..4400f69
--- /dev/null
+++ b/cc/metrics/event_metrics_unittest.cc
@@ -0,0 +1,303 @@
+// Copyright 2022 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 "base/test/simple_test_tick_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+class EventMetricsTest : public testing::Test {
+ private:
+  base::SimpleTestTickClock test_tick_clock_;
+
+ protected:
+  base::TimeTicks AdvanceNowByMs(int advance_ms) {
+    test_tick_clock_.Advance(base::Microseconds(advance_ms));
+    return test_tick_clock_.NowTicks();
+  }
+};
+
+TEST_F(EventMetricsTest, ScrollBeginCreateWithNullBeginRwhTime) {
+  // Arrange
+  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
+  base::TimeTicks arrived_in_browser_main_timestamp;
+  base::TimeTicks now = base::TimeTicks::Now();
+
+  // Act
+  std::unique_ptr<ScrollEventMetrics> scroll_event_metric =
+      ScrollEventMetrics::Create(
+          ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kTouchscreen,
+          /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp);
+
+  // Assert
+  EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp(
+                            EventMetrics::DispatchStage::kGenerated));
+  EXPECT_LE(now,
+            scroll_event_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
+  // not set
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kArrivedInBrowserMain)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererCompositorStarted)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererCompositorFinished)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererMainStarted)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererMainFinished)
+                  .is_null());
+}
+
+TEST_F(EventMetricsTest, ScrollBeginCreate) {
+  // Arrange
+  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
+  base::TimeTicks arrived_in_browser_main_timestamp =
+      base::TimeTicks::Now() - base::Microseconds(50);
+  base::TimeTicks now = base::TimeTicks::Now();
+
+  // Act
+  std::unique_ptr<ScrollEventMetrics> scroll_event_metric =
+      ScrollEventMetrics::Create(
+          ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kTouchscreen,
+          /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp);
+
+  // Assert
+  EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp(
+                            EventMetrics::DispatchStage::kGenerated));
+  EXPECT_EQ(arrived_in_browser_main_timestamp,
+            scroll_event_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInBrowserMain));
+  EXPECT_LE(now,
+            scroll_event_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
+  // not set
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererCompositorStarted)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererCompositorFinished)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererMainStarted)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererMainFinished)
+                  .is_null());
+}
+
+TEST_F(EventMetricsTest, ScrollBeginCreateFromExisting) {
+  // Arrange
+  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
+  base::TimeTicks arrived_in_browser_main_timestamp =
+      base::TimeTicks::Now() - base::Microseconds(50);
+  std::unique_ptr<ScrollEventMetrics> scroll_metric =
+      ScrollEventMetrics::Create(
+          ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kTouchscreen,
+          /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp);
+
+  // Act
+  std::unique_ptr<ScrollEventMetrics> copy_scroll_metric =
+      ScrollEventMetrics::CreateFromExisting(
+          ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kTouchscreen,
+          /*is_inertial=*/false,
+          EventMetrics::DispatchStage::kRendererMainFinished,
+          scroll_metric.get());
+
+  // Assert
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kGenerated),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kGenerated));
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInBrowserMain),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInBrowserMain));
+
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInRendererCompositor),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
+
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererCompositorStarted),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererCompositorStarted));
+
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererCompositorFinished),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererCompositorFinished));
+
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererMainStarted),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererMainStarted));
+
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererMainFinished),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererMainFinished));
+}
+
+TEST_F(EventMetricsTest, ScrollUpdateCreateWithNullBeginRwhTime) {
+  // Arrange
+  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
+  base::TimeTicks arrived_in_browser_main_timestamp;
+  base::TimeTicks now = base::TimeTicks::Now();
+
+  // Act
+  std::unique_ptr<ScrollUpdateEventMetrics> scroll_event_metric =
+      ScrollUpdateEventMetrics::Create(
+          ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kTouchscreen,
+          /*is_inertial=*/false,
+          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4,
+          event_time, arrived_in_browser_main_timestamp);
+
+  // Assert
+  EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp(
+                            EventMetrics::DispatchStage::kGenerated));
+  EXPECT_LE(now,
+            scroll_event_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
+  // not set
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kArrivedInBrowserMain)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererCompositorStarted)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererCompositorFinished)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererMainStarted)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererMainFinished)
+                  .is_null());
+}
+
+TEST_F(EventMetricsTest, ScrollUpdateCreate) {
+  // Arrange
+  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
+  base::TimeTicks arrived_in_browser_main_timestamp =
+      base::TimeTicks::Now() - base::Microseconds(50);
+  base::TimeTicks now = base::TimeTicks::Now();
+
+  // Act
+  std::unique_ptr<ScrollUpdateEventMetrics> scroll_event_metric =
+      ScrollUpdateEventMetrics::Create(
+          ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kTouchscreen,
+          /*is_inertial=*/false,
+          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4,
+          event_time, arrived_in_browser_main_timestamp);
+
+  // Assert
+  EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp(
+                            EventMetrics::DispatchStage::kGenerated));
+  EXPECT_EQ(arrived_in_browser_main_timestamp,
+            scroll_event_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInBrowserMain));
+  EXPECT_LE(now,
+            scroll_event_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
+  // not set
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererCompositorStarted)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererCompositorFinished)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererMainStarted)
+                  .is_null());
+  EXPECT_TRUE(scroll_event_metric
+                  ->GetDispatchStageTimestamp(
+                      EventMetrics::DispatchStage::kRendererMainFinished)
+                  .is_null());
+}
+
+TEST_F(EventMetricsTest, ScrollUpdateCreateFromExisting) {
+  // Arrange
+  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
+  base::TimeTicks arrived_in_browser_main_timestamp =
+      base::TimeTicks::Now() - base::Microseconds(50);
+  std::unique_ptr<ScrollUpdateEventMetrics> scroll_metric =
+      ScrollUpdateEventMetrics::Create(
+          ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kTouchscreen,
+          /*is_inertial=*/false,
+          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4,
+          event_time, arrived_in_browser_main_timestamp);
+
+  // Act
+  std::unique_ptr<ScrollUpdateEventMetrics> copy_scroll_metric =
+      ScrollUpdateEventMetrics::CreateFromExisting(
+          ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kTouchscreen,
+          /*is_inertial=*/false,
+          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4,
+          EventMetrics::DispatchStage::kRendererMainFinished,
+          scroll_metric.get());
+
+  // Assert
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kGenerated),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kGenerated));
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInBrowserMain),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInBrowserMain));
+
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInRendererCompositor),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
+
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererCompositorStarted),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererCompositorStarted));
+
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererCompositorFinished),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererCompositorFinished));
+
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererMainStarted),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererMainStarted));
+
+  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererMainFinished),
+            copy_scroll_metric->GetDispatchStageTimestamp(
+                EventMetrics::DispatchStage::kRendererMainFinished));
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index b717ef9..37801bf 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -14249,7 +14249,8 @@
         /*is_inertial=*/false,
         i == 0 ? ScrollUpdateEventMetrics::ScrollUpdateType::kStarted
                : ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
-        /*delta=*/10.0f, base::TimeTicks::Now()));
+        /*delta=*/10.0f, base::TimeTicks::Now(),
+        base::TimeTicks::Now() + base::Milliseconds(1)));
     host_impl_->active_tree()->AppendEventsMetricsFromMainThread(
         std::move(events_metrics));
 
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index fca6e54..57ec342 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -9279,12 +9279,15 @@
     tick_clock.Advance(base::Microseconds(10));
     base::TimeTicks event_time = tick_clock.NowTicks();
     tick_clock.Advance(base::Microseconds(10));
+    base::TimeTicks arrived_in_browser_main_timestamp = tick_clock.NowTicks();
+    tick_clock.Advance(base::Microseconds(10));
     std::unique_ptr<EventMetrics> metrics =
         ScrollUpdateEventMetrics::CreateForTesting(
             ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kWheel,
             /*is_inertial=*/false,
             ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
-            /*delta=*/10.0f, event_time, &tick_clock);
+            /*delta=*/10.0f, event_time, arrived_in_browser_main_timestamp,
+            &tick_clock);
     DCHECK_NE(metrics, nullptr);
     {
       tick_clock.Advance(base::Microseconds(10));
diff --git a/cc/trees/ukm_manager.cc b/cc/trees/ukm_manager.cc
index 6d8e854..2a87b60 100644
--- a/cc/trees/ukm_manager.cc
+++ b/cc/trees/ukm_manager.cc
@@ -293,9 +293,26 @@
           (end_timestamp - dispatch_timestamp).InMicroseconds();
       switch (dispatch_stage) {
         case EventMetrics::DispatchStage::kGenerated:
+          switch (end_stage) {
+            case EventMetrics::DispatchStage::kArrivedInBrowserMain:
+              // Will build the `GenerationToRendererCompositor` metric on the
+              // `kArrivedInBrowserMain` stage.
+              break;
+            case EventMetrics::DispatchStage::kArrivedInRendererCompositor:
+              builder.SetGenerationToRendererCompositor(dispatch_latency);
+              break;
+            default:
+              NOTREACHED();
+              break;
+          }
+          break;
+        case EventMetrics::DispatchStage::kArrivedInBrowserMain:
           DCHECK_EQ(end_stage,
                     EventMetrics::DispatchStage::kArrivedInRendererCompositor);
-          builder.SetGenerationToRendererCompositor(dispatch_latency);
+          // TODO(b/224960731): Add new UKM metrics and then split kGenerated
+          // with kArrivedInBrowserMain breakdown.
+          builder.SetGenerationToRendererCompositor(
+              (end_timestamp - generated_timestamp).InMicroseconds());
           break;
         case EventMetrics::DispatchStage::kArrivedInRendererCompositor:
           switch (end_stage) {
diff --git a/cc/trees/ukm_manager_unittest.cc b/cc/trees/ukm_manager_unittest.cc
index abb3a9b..cc8b31e 100644
--- a/cc/trees/ukm_manager_unittest.cc
+++ b/cc/trees/ukm_manager_unittest.cc
@@ -150,20 +150,24 @@
 
   std::unique_ptr<EventMetrics> CreateScrollBeginEventMetrics() {
     base::TimeTicks event_time = AdvanceNowByMs(10);
+    base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByMs(5);
     AdvanceNowByMs(10);
     return SetupEventMetrics(ScrollEventMetrics::CreateForTesting(
         ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kWheel,
-        /*is_inertial=*/false, event_time, &test_tick_clock_));
+        /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp,
+        &test_tick_clock_));
   }
 
   std::unique_ptr<EventMetrics> CreateScrollUpdateEventMetrics(
       bool is_inertial,
       ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type) {
     base::TimeTicks event_time = AdvanceNowByMs(10);
+    base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByMs(5);
     AdvanceNowByMs(10);
     return SetupEventMetrics(ScrollUpdateEventMetrics::CreateForTesting(
         ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kWheel, is_inertial,
-        scroll_update_type, /*delta=*/10.0f, event_time, &test_tick_clock_));
+        scroll_update_type, /*delta=*/10.0f, event_time,
+        arrived_in_browser_main_timestamp, &test_tick_clock_));
   }
 
   struct DispatchTimestamps {
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
index 1c4ff88..ce938833 100644
--- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
+++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
@@ -594,6 +594,21 @@
         static_cast<const WebGestureEvent&>(event->Event());
     const bool is_inertial = gesture_event.InertialPhase() ==
                              WebGestureEvent::InertialPhaseState::kMomentum;
+
+    // TODO(b/224960731): It is not recommended to use LatencyInfo. So we need
+    // to create a separate field with "arrived_in_browser_main" timestamp in
+    // WebInputEvent and use it here.
+    base::TimeTicks arrived_in_browser_main_timestamp;
+    event->latency_info().FindLatency(
+        ui::LatencyComponentType::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT,
+        &(arrived_in_browser_main_timestamp));
+    // TODO(b/224960731): Fix tests and add
+    // `DCHECK(!arrived_in_browser_main_timestamp.is_null())`.
+    //  We expect that `INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT` is always
+    //  found, but there are a lot of tests where this component is not set.
+    //  Currently EventMetrics knows how to handle null timestamp, so we
+    //  don't process it here.
+
     if (gesture_event.GetType() == WebInputEvent::Type::kGestureScrollUpdate) {
       metrics = cc::ScrollUpdateEventMetrics::Create(
           gesture_event.GetTypeAsUiEventType(),
@@ -601,13 +616,14 @@
           has_seen_first_gesture_scroll_update_after_begin_
               ? cc::ScrollUpdateEventMetrics::ScrollUpdateType::kContinued
               : cc::ScrollUpdateEventMetrics::ScrollUpdateType::kStarted,
-          gesture_event.data.scroll_update.delta_y, event->Event().TimeStamp());
+          gesture_event.data.scroll_update.delta_y, event->Event().TimeStamp(),
+          arrived_in_browser_main_timestamp);
       has_seen_first_gesture_scroll_update_after_begin_ = true;
     } else {
       metrics = cc::ScrollEventMetrics::Create(
           gesture_event.GetTypeAsUiEventType(),
           gesture_event.GetScrollInputType(), is_inertial,
-          event->Event().TimeStamp());
+          event->Event().TimeStamp(), arrived_in_browser_main_timestamp);
       has_seen_first_gesture_scroll_update_after_begin_ = false;
     }
   } else if (WebInputEvent::IsPinchGestureEventType(event->Event().GetType())) {
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index 41bb845..4ebcbde 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -93,8 +93,7 @@
 // WindowEventDispatcher, public:
 
 WindowEventDispatcher::WindowEventDispatcher(WindowTreeHost* host)
-    : host_(host),
-      event_targeter_(std::make_unique<WindowTargeter>()) {
+    : host_(host), event_targeter_(std::make_unique<WindowTargeter>()) {
   Env::GetInstance()->gesture_recognizer()->AddGestureEventHelper(this);
   Env::GetInstance()->AddObserver(this);
 }
@@ -1096,7 +1095,7 @@
             ? ui::ScrollInputType::kTouchscreen
             : ui::ScrollInputType::kWheel;
     if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
-      metrics = cc::ScrollUpdateEventMetrics::Create(
+      metrics = cc::ScrollUpdateEventMetrics::CreateForBrowser(
           ui::ET_GESTURE_SCROLL_UPDATE, input_type, /*is_inertial=*/false,
           has_seen_gesture_scroll_update_after_begin_
               ? cc::ScrollUpdateEventMetrics::ScrollUpdateType::kContinued
@@ -1104,9 +1103,9 @@
           gesture->details().scroll_y(), gesture->time_stamp());
       has_seen_gesture_scroll_update_after_begin_ = true;
     } else if (gesture->IsScrollGestureEvent()) {
-      metrics = cc::ScrollEventMetrics::Create(gesture->type(), input_type,
-                                               /*is_inertial=*/false,
-                                               gesture->time_stamp());
+      metrics = cc::ScrollEventMetrics::CreateForBrowser(
+          gesture->type(), input_type,
+          /*is_inertial=*/false, gesture->time_stamp());
       if (gesture->type() == ui::ET_GESTURE_SCROLL_BEGIN)
         has_seen_gesture_scroll_update_after_begin_ = false;
     } else {