1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h"
6 
7 #include "base/time/time.h"
8 #include "components/page_load_metrics/browser/page_load_metrics_observer.h"
9 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
10 #include "components/page_load_metrics/common/page_load_timing.h"
11 #include "content/public/browser/navigation_handle.h"
12 #include "services/metrics/public/cpp/ukm_builders.h"
13 #include "services/metrics/public/cpp/ukm_recorder.h"
14 #include "services/metrics/public/cpp/ukm_source.h"
15 
ForegroundDurationUKMObserver()16 ForegroundDurationUKMObserver::ForegroundDurationUKMObserver()
17     : last_page_input_timing_(page_load_metrics::mojom::InputTiming()) {}
18 
~ForegroundDurationUKMObserver()19 ForegroundDurationUKMObserver::~ForegroundDurationUKMObserver() {}
20 
21 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
OnStart(content::NavigationHandle * navigation_handle,const GURL & currently_committed_url,bool started_in_foreground)22 ForegroundDurationUKMObserver::OnStart(
23     content::NavigationHandle* navigation_handle,
24     const GURL& currently_committed_url,
25     bool started_in_foreground) {
26   currently_in_foreground_ = started_in_foreground;
27   if (currently_in_foreground_) {
28     last_time_shown_ = navigation_handle->NavigationStart();
29   }
30   return CONTINUE_OBSERVING;
31 }
32 
33 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
OnCommit(content::NavigationHandle * navigation_handle,ukm::SourceId source_id)34 ForegroundDurationUKMObserver::OnCommit(
35     content::NavigationHandle* navigation_handle,
36     ukm::SourceId source_id) {
37   source_id_ = source_id;
38   return CONTINUE_OBSERVING;
39 }
40 
41 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
FlushMetricsOnAppEnterBackground(const page_load_metrics::mojom::PageLoadTiming & timing)42 ForegroundDurationUKMObserver::FlushMetricsOnAppEnterBackground(
43     const page_load_metrics::mojom::PageLoadTiming& timing) {
44   RecordUkmIfInForeground(base::TimeTicks::Now());
45   return CONTINUE_OBSERVING;
46 }
47 
48 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
OnHidden(const page_load_metrics::mojom::PageLoadTiming & timing)49 ForegroundDurationUKMObserver::OnHidden(
50     const page_load_metrics::mojom::PageLoadTiming& timing) {
51   RecordUkmIfInForeground(base::TimeTicks::Now());
52   return CONTINUE_OBSERVING;
53 }
54 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
OnShown()55 ForegroundDurationUKMObserver::OnShown() {
56   if (!currently_in_foreground_) {
57     last_time_shown_ = base::TimeTicks::Now();
58     currently_in_foreground_ = true;
59   }
60   return CONTINUE_OBSERVING;
61 }
62 
OnComplete(const page_load_metrics::mojom::PageLoadTiming & timing)63 void ForegroundDurationUKMObserver::OnComplete(
64     const page_load_metrics::mojom::PageLoadTiming& timing) {
65   // If we have a page_end_time, use it as our end time, else fall back to the
66   // current time. Note that we expect page_end_time.has_value() to always be
67   // true in OnComplete (the PageLoadTracker destructor is supposed to guarantee
68   // it), but we use Now() as a graceful fallback just in case.
69   base::TimeTicks end_time = GetDelegate().GetPageEndTime().has_value()
70                                  ? GetDelegate().GetNavigationStart() +
71                                        GetDelegate().GetPageEndTime().value()
72                                  : base::TimeTicks::Now();
73   RecordUkmIfInForeground(end_time);
74 }
75 
RecordUkmIfInForeground(base::TimeTicks end_time)76 void ForegroundDurationUKMObserver::RecordUkmIfInForeground(
77     base::TimeTicks end_time) {
78   if (!currently_in_foreground_)
79     return;
80   base::TimeDelta foreground_duration = end_time - last_time_shown_;
81   ukm::builders::PageForegroundSession ukm_builder(source_id_);
82   ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
83   ukm_builder.SetForegroundDuration(foreground_duration.InMilliseconds());
84   RecordInputTimingMetrics(&ukm_builder);
85   ukm_builder.Record(ukm_recorder);
86   currently_in_foreground_ = false;
87 }
88 
RecordInputTimingMetrics(ukm::builders::PageForegroundSession * ukm_builder)89 void ForegroundDurationUKMObserver::RecordInputTimingMetrics(
90     ukm::builders::PageForegroundSession* ukm_builder) {
91   // TODO(hbsong): crbug.com/1105665
92   if (GetDelegate().GetPageInputTiming().total_input_delay.InMilliseconds() < 0)
93     return;
94 
95   ukm_builder
96       ->SetForegroundNumInputEvents(
97           GetDelegate().GetPageInputTiming().num_input_events -
98           last_page_input_timing_.num_input_events)
99       .SetForegroundTotalInputDelay(
100           (GetDelegate().GetPageInputTiming().total_input_delay -
101            last_page_input_timing_.total_input_delay)
102               .InMilliseconds())
103       .SetForegroundTotalAdjustedInputDelay(
104           (GetDelegate().GetPageInputTiming().total_adjusted_input_delay -
105            last_page_input_timing_.total_adjusted_input_delay)
106               .InMilliseconds());
107   last_page_input_timing_ = GetDelegate().GetPageInputTiming();
108 }
109