1 // Copyright 2017 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/core/ukm_page_load_metrics_observer.h"
6 
7 #include <cmath>
8 #include <memory>
9 #include <vector>
10 
11 #include "base/feature_list.h"
12 #include "base/timer/elapsed_timer.h"
13 #include "base/trace_event/common/trace_event_common.h"
14 #include "cc/metrics/ukm_smoothness_data.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/content_settings/cookie_settings_factory.h"
17 #include "chrome/browser/engagement/site_engagement_service.h"
18 #include "chrome/browser/prefetch/no_state_prefetch/prerender_manager_factory.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/search_engines/template_url_service_factory.h"
21 #include "chrome/common/pref_names.h"
22 #include "components/content_settings/core/browser/cookie_settings.h"
23 #include "components/content_settings/core/common/features.h"
24 #include "components/content_settings/core/common/pref_names.h"
25 #include "components/metrics/net/network_metrics_provider.h"
26 #include "components/no_state_prefetch/browser/prerender_manager.h"
27 #include "components/no_state_prefetch/browser/prerender_util.h"
28 #include "components/no_state_prefetch/common/prerender_final_status.h"
29 #include "components/no_state_prefetch/common/prerender_origin.h"
30 #include "components/offline_pages/buildflags/buildflags.h"
31 #include "components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h"
32 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
33 #include "components/page_load_metrics/browser/protocol_util.h"
34 #include "components/prefs/pref_service.h"
35 #include "components/search_engines/template_url_service.h"
36 #include "content/public/browser/navigation_entry.h"
37 #include "content/public/browser/web_contents.h"
38 #include "media/base/mime_util.h"
39 #include "net/base/load_timing_info.h"
40 #include "net/http/http_response_headers.h"
41 #include "services/metrics/public/cpp/metrics_utils.h"
42 #include "services/metrics/public/cpp/ukm_builders.h"
43 #include "services/metrics/public/cpp/ukm_recorder.h"
44 #include "services/network/public/cpp/network_quality_tracker.h"
45 #include "third_party/blink/public/common/mime_util/mime_util.h"
46 #include "third_party/metrics_proto/system_profile.pb.h"
47 #include "ui/events/blink/blink_features.h"
48 
49 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
50 #include "chrome/browser/offline_pages/offline_page_tab_helper.h"
51 #endif
52 
53 namespace {
54 
55 const char kOfflinePreviewsMimeType[] = "multipart/related";
56 extern const base::Feature kLayoutShiftNormalizationRecordUKM{
57     "LayoutShiftNormalizationRecordUKM", base::FEATURE_DISABLED_BY_DEFAULT};
58 
IsSupportedProtocol(page_load_metrics::NetworkProtocol protocol)59 bool IsSupportedProtocol(page_load_metrics::NetworkProtocol protocol) {
60   switch (protocol) {
61     case page_load_metrics::NetworkProtocol::kHttp11:
62       return true;
63     case page_load_metrics::NetworkProtocol::kHttp2:
64       return true;
65     case page_load_metrics::NetworkProtocol::kQuic:
66       return true;
67     case page_load_metrics::NetworkProtocol::kOther:
68       return false;
69   }
70 }
71 
IsDefaultSearchEngine(content::BrowserContext * browser_context,const GURL & url)72 bool IsDefaultSearchEngine(content::BrowserContext* browser_context,
73                            const GURL& url) {
74   if (!browser_context)
75     return false;
76 
77   auto* template_service = TemplateURLServiceFactory::GetForProfile(
78       Profile::FromBrowserContext(browser_context));
79 
80   if (!template_service)
81     return false;
82 
83   return template_service->IsSearchResultsPageFromDefaultSearchProvider(url);
84 }
85 
IsUserHomePage(content::BrowserContext * browser_context,const GURL & url)86 bool IsUserHomePage(content::BrowserContext* browser_context, const GURL& url) {
87   if (!browser_context)
88     return false;
89 
90   return url.spec() == Profile::FromBrowserContext(browser_context)
91                            ->GetPrefs()
92                            ->GetString(prefs::kHomePage);
93 }
94 
CumulativeShiftScoreTraceData(float layout_shift_score,float layout_shift_score_before_input_or_scroll)95 std::unique_ptr<base::trace_event::TracedValue> CumulativeShiftScoreTraceData(
96     float layout_shift_score,
97     float layout_shift_score_before_input_or_scroll) {
98   std::unique_ptr<base::trace_event::TracedValue> data =
99       std::make_unique<base::trace_event::TracedValue>();
100   data->SetDouble("layoutShiftScore", layout_shift_score);
101   data->SetDouble("layoutShiftScoreBeforeInputOrScroll",
102                   layout_shift_score_before_input_or_scroll);
103   return data;
104 }
105 
SiteInstanceRenderProcessAssignmentToInt(content::SiteInstanceProcessAssignment assignment)106 int SiteInstanceRenderProcessAssignmentToInt(
107     content::SiteInstanceProcessAssignment assignment) {
108   // These values are logged in UKM and should not be reordered or changed. Add
109   // new values to the end and be sure to update the enum
110   // |SiteInstanceProcessAssignment| in
111   // //tools/metrics/histograms/enums.xml.
112   switch (assignment) {
113     case content::SiteInstanceProcessAssignment::UNKNOWN:
114       return 0;
115     case content::SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS:
116       return 1;
117     case content::SiteInstanceProcessAssignment::USED_SPARE_PROCESS:
118       return 2;
119     case content::SiteInstanceProcessAssignment::CREATED_NEW_PROCESS:
120       return 3;
121   }
122   return 0;
123 }
124 
BucketWithOffsetAndUnit(int num,int offset,uint32_t unit)125 int BucketWithOffsetAndUnit(int num, int offset, uint32_t unit) {
126   // Bucketing raw number with `offset` centered.
127   const int grid = (num - offset) / unit;
128   const int bucketed =
129       grid == 0 ? 0
130                 : grid > 0 ? std::pow(2, static_cast<int>(std::log2(grid)))
131                            : -std::pow(2, static_cast<int>(std::log2(-grid)));
132   return bucketed * unit + offset;
133 }
134 
135 }  // namespace
136 
137 // static
138 std::unique_ptr<page_load_metrics::PageLoadMetricsObserver>
CreateIfNeeded()139 UkmPageLoadMetricsObserver::CreateIfNeeded() {
140   if (!ukm::UkmRecorder::Get()) {
141     return nullptr;
142   }
143   return std::make_unique<UkmPageLoadMetricsObserver>(
144       g_browser_process->network_quality_tracker());
145 }
146 
UkmPageLoadMetricsObserver(network::NetworkQualityTracker * network_quality_tracker)147 UkmPageLoadMetricsObserver::UkmPageLoadMetricsObserver(
148     network::NetworkQualityTracker* network_quality_tracker)
149     : network_quality_tracker_(network_quality_tracker) {
150   DCHECK(network_quality_tracker_);
151 }
152 
153 UkmPageLoadMetricsObserver::~UkmPageLoadMetricsObserver() = default;
154 
OnStart(content::NavigationHandle * navigation_handle,const GURL & currently_committed_url,bool started_in_foreground)155 UkmPageLoadMetricsObserver::ObservePolicy UkmPageLoadMetricsObserver::OnStart(
156     content::NavigationHandle* navigation_handle,
157     const GURL& currently_committed_url,
158     bool started_in_foreground) {
159   content::WebContents* web_contents = navigation_handle->GetWebContents();
160   is_portal_ = web_contents->IsPortal();
161 
162   browser_context_ = web_contents->GetBrowserContext();
163 
164   start_url_is_default_search_ =
165       IsDefaultSearchEngine(browser_context_, navigation_handle->GetURL());
166   start_url_is_home_page_ =
167       IsUserHomePage(browser_context_, navigation_handle->GetURL());
168 
169   if (started_in_foreground) {
170     last_time_shown_ = navigation_handle->NavigationStart();
171   }
172   currently_in_foreground_ = started_in_foreground;
173 
174   if (!started_in_foreground) {
175     was_hidden_ = true;
176     return CONTINUE_OBSERVING;
177   }
178 
179   // When OnStart is invoked, we don't yet know whether we're observing a web
180   // page load, vs another kind of load (e.g. a download or a PDF). Thus,
181   // metrics and source information should not be recorded here. Instead, we
182   // store data we might want to persist in member variables below, and later
183   // record UKM metrics for that data once we've confirmed that we're observing
184   // a web page load.
185 
186   effective_connection_type_ =
187       network_quality_tracker_->GetEffectiveConnectionType();
188   http_rtt_estimate_ = network_quality_tracker_->GetHttpRTT();
189   transport_rtt_estimate_ = network_quality_tracker_->GetTransportRTT();
190   downstream_kbps_estimate_ =
191       network_quality_tracker_->GetDownstreamThroughputKbps();
192   page_transition_ = navigation_handle->GetPageTransition();
193   return CONTINUE_OBSERVING;
194 }
195 
196 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
OnRedirect(content::NavigationHandle * navigation_handle)197 UkmPageLoadMetricsObserver::OnRedirect(
198     content::NavigationHandle* navigation_handle) {
199   main_frame_request_redirect_count_++;
200   return CONTINUE_OBSERVING;
201 }
202 
203 UkmPageLoadMetricsObserver::ObservePolicy
ShouldObserveMimeType(const std::string & mime_type) const204 UkmPageLoadMetricsObserver::ShouldObserveMimeType(
205     const std::string& mime_type) const {
206   if (PageLoadMetricsObserver::ShouldObserveMimeType(mime_type) ==
207           CONTINUE_OBSERVING ||
208       mime_type == kOfflinePreviewsMimeType) {
209     return CONTINUE_OBSERVING;
210   }
211   return STOP_OBSERVING;
212 }
213 
OnCommit(content::NavigationHandle * navigation_handle,ukm::SourceId source_id)214 UkmPageLoadMetricsObserver::ObservePolicy UkmPageLoadMetricsObserver::OnCommit(
215     content::NavigationHandle* navigation_handle,
216     ukm::SourceId source_id) {
217   if (navigation_handle->GetWebContents()->GetContentsMimeType() ==
218       kOfflinePreviewsMimeType) {
219     if (!IsOfflinePreview(navigation_handle->GetWebContents()))
220       return STOP_OBSERVING;
221   }
222   connection_info_ = navigation_handle->GetConnectionInfo();
223   const net::HttpResponseHeaders* response_headers =
224       navigation_handle->GetResponseHeaders();
225   if (response_headers)
226     http_response_code_ = response_headers->response_code();
227   // The PageTransition for the navigation may be updated on commit.
228   page_transition_ = navigation_handle->GetPageTransition();
229   was_cached_ = navigation_handle->WasResponseCached();
230   navigation_handle_timing_ = navigation_handle->GetNavigationHandleTiming();
231   prerender::PrerenderManager* const prerender_manager =
232       prerender::PrerenderManagerFactory::GetForBrowserContext(
233           navigation_handle->GetWebContents()->GetBrowserContext());
234   if (prerender_manager) {
235     prerender::RecordNoStatePrefetchMetrics(navigation_handle, source_id,
236                                             prerender_manager);
237   }
238   RecordGeneratedNavigationUKM(source_id, navigation_handle->GetURL());
239   navigation_is_cross_process_ = !navigation_handle->IsSameProcess();
240   navigation_entry_offset_ = navigation_handle->GetNavigationEntryOffset();
241   main_document_sequence_number_ = navigation_handle->GetWebContents()
242                                        ->GetController()
243                                        .GetLastCommittedEntry()
244                                        ->GetMainFrameDocumentSequenceNumber();
245 
246   render_process_assignment_ = navigation_handle->GetWebContents()
247                                    ->GetMainFrame()
248                                    ->GetSiteInstance()
249                                    ->GetLastProcessAssignmentOutcome();
250 
251   return CONTINUE_OBSERVING;
252 }
253 
254 UkmPageLoadMetricsObserver::ObservePolicy
FlushMetricsOnAppEnterBackground(const page_load_metrics::mojom::PageLoadTiming & timing)255 UkmPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
256     const page_load_metrics::mojom::PageLoadTiming& timing) {
257   if (is_portal_)
258     return STOP_OBSERVING;
259 
260   base::TimeTicks current_time = base::TimeTicks::Now();
261   if (!was_hidden_) {
262     RecordNavigationTimingMetrics();
263     RecordPageLoadMetrics(current_time);
264     RecordRendererUsageMetrics();
265     RecordTimingMetrics(timing);
266     RecordInputTimingMetrics();
267   }
268   ReportLayoutStability();
269   RecordSmoothnessMetrics();
270   // Assume that page ends on this method, as the app could be evicted right
271   // after.
272   RecordPageEndMetrics(&timing, current_time);
273   return STOP_OBSERVING;
274 }
275 
OnHidden(const page_load_metrics::mojom::PageLoadTiming & timing)276 UkmPageLoadMetricsObserver::ObservePolicy UkmPageLoadMetricsObserver::OnHidden(
277     const page_load_metrics::mojom::PageLoadTiming& timing) {
278   if (is_portal_)
279     return CONTINUE_OBSERVING;
280 
281   if (currently_in_foreground_ && !last_time_shown_.is_null()) {
282     total_foreground_duration_ += base::TimeTicks::Now() - last_time_shown_;
283   }
284   currently_in_foreground_ = false;
285   if (!was_hidden_) {
286     RecordNavigationTimingMetrics();
287     RecordPageLoadMetrics(base::TimeTicks() /* no app_background_time */);
288     RecordRendererUsageMetrics();
289     RecordTimingMetrics(timing);
290     RecordInputTimingMetrics();
291     was_hidden_ = true;
292   }
293   return CONTINUE_OBSERVING;
294 }
295 
296 UkmPageLoadMetricsObserver::ObservePolicy
OnShown()297 UkmPageLoadMetricsObserver::OnShown() {
298   if (is_portal_)
299     return CONTINUE_OBSERVING;
300 
301   currently_in_foreground_ = true;
302   last_time_shown_ = base::TimeTicks::Now();
303   return CONTINUE_OBSERVING;
304 }
305 
OnFailedProvisionalLoad(const page_load_metrics::FailedProvisionalLoadInfo & failed_load_info)306 void UkmPageLoadMetricsObserver::OnFailedProvisionalLoad(
307     const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info) {
308   if (is_portal_)
309     return;
310 
311   RecordPageEndMetrics(nullptr, base::TimeTicks());
312   if (was_hidden_)
313     return;
314 
315   RecordPageLoadMetrics(base::TimeTicks() /* no app_background_time */);
316 
317   RecordRendererUsageMetrics();
318 
319   // Error codes have negative values, however we log net error code enum values
320   // for UMA histograms using the equivalent positive value. For consistency in
321   // UKM, we convert to a positive value here.
322   int64_t net_error_code = static_cast<int64_t>(failed_load_info.error) * -1;
323   DCHECK_GE(net_error_code, 0);
324   ukm::builders::PageLoad(GetDelegate().GetPageUkmSourceId())
325       .SetNet_ErrorCode_OnFailedProvisionalLoad(net_error_code)
326       .SetPageTiming_NavigationToFailedProvisionalLoad(
327           failed_load_info.time_to_failed_provisional_load.InMilliseconds())
328       .Record(ukm::UkmRecorder::Get());
329 }
330 
OnComplete(const page_load_metrics::mojom::PageLoadTiming & timing)331 void UkmPageLoadMetricsObserver::OnComplete(
332     const page_load_metrics::mojom::PageLoadTiming& timing) {
333   if (is_portal_)
334     return;
335 
336   base::TimeTicks current_time = base::TimeTicks::Now();
337   if (!was_hidden_) {
338     RecordNavigationTimingMetrics();
339     RecordPageLoadMetrics(current_time /* no app_background_time */);
340     RecordRendererUsageMetrics();
341     RecordTimingMetrics(timing);
342     RecordInputTimingMetrics();
343   }
344   ReportLayoutStability();
345   RecordSmoothnessMetrics();
346   ReportPerfectHeuristicsMetrics();
347   RecordPageEndMetrics(&timing, current_time);
348   RecordMobileFriendlinessMetrics();
349 }
350 
OnResourceDataUseObserved(content::RenderFrameHost * content,const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> & resources)351 void UkmPageLoadMetricsObserver::OnResourceDataUseObserved(
352     content::RenderFrameHost* content,
353     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
354         resources) {
355   if (was_hidden_)
356     return;
357   for (auto const& resource : resources) {
358     network_bytes_ += resource->delta_bytes;
359 
360     if (blink::IsSupportedImageMimeType(resource->mime_type)) {
361       image_total_bytes_ += resource->delta_bytes;
362       if (!resource->is_main_frame_resource)
363         image_subframe_bytes_ += resource->delta_bytes;
364     } else if (media::IsSupportedMediaMimeType(resource->mime_type) ||
365                base::StartsWith(resource->mime_type, "audio/",
366                                 base::CompareCase::SENSITIVE) ||
367                base::StartsWith(resource->mime_type, "video/",
368                                 base::CompareCase::SENSITIVE)) {
369       media_bytes_ += resource->delta_bytes;
370     }
371 
372     // Only sum body lengths for completed resources.
373     if (!resource->is_complete)
374       continue;
375     if (blink::IsSupportedJavascriptMimeType(resource->mime_type)) {
376       js_decoded_bytes_ += resource->decoded_body_length;
377       if (resource->decoded_body_length > js_max_decoded_bytes_)
378         js_max_decoded_bytes_ = resource->decoded_body_length;
379     }
380     if (resource->cache_type !=
381         page_load_metrics::mojom::CacheType::kNotCached) {
382       cache_bytes_ += resource->encoded_body_length;
383     }
384   }
385 }
386 
OnLoadedResource(const page_load_metrics::ExtraRequestCompleteInfo & extra_request_complete_info)387 void UkmPageLoadMetricsObserver::OnLoadedResource(
388     const page_load_metrics::ExtraRequestCompleteInfo&
389         extra_request_complete_info) {
390   if (was_hidden_)
391     return;
392   if (extra_request_complete_info.request_destination ==
393       network::mojom::RequestDestination::kDocument) {
394     DCHECK(!main_frame_timing_.has_value());
395     main_frame_timing_ = *extra_request_complete_info.load_timing_info;
396   }
397 }
398 
RecordNavigationTimingMetrics()399 void UkmPageLoadMetricsObserver::RecordNavigationTimingMetrics() {
400   const base::TimeTicks navigation_start_time =
401       GetDelegate().GetNavigationStart();
402   const content::NavigationHandleTiming& timing = navigation_handle_timing_;
403 
404   // Record metrics for navigation only when all relevant milestones are
405   // recorded and in the expected order. It is allowed that they have the same
406   // value for some cases (e.g., internal redirection for HSTS).
407   if (navigation_start_time.is_null() ||
408       timing.first_request_start_time.is_null() ||
409       timing.first_response_start_time.is_null() ||
410       timing.first_loader_callback_time.is_null() ||
411       timing.final_request_start_time.is_null() ||
412       timing.final_response_start_time.is_null() ||
413       timing.final_loader_callback_time.is_null() ||
414       timing.navigation_commit_sent_time.is_null()) {
415     return;
416   }
417   // TODO(https://crbug.com/1076710): Change these early-returns to DCHECKs
418   // after the issue 1076710 is fixed.
419   if (navigation_start_time > timing.first_request_start_time ||
420       timing.first_request_start_time > timing.first_response_start_time ||
421       timing.first_response_start_time > timing.first_loader_callback_time ||
422       timing.first_loader_callback_time > timing.navigation_commit_sent_time) {
423     return;
424   }
425   if (navigation_start_time > timing.final_request_start_time ||
426       timing.final_request_start_time > timing.final_response_start_time ||
427       timing.final_response_start_time > timing.final_loader_callback_time ||
428       timing.final_loader_callback_time > timing.navigation_commit_sent_time) {
429     return;
430   }
431   DCHECK_LE(timing.first_request_start_time, timing.final_request_start_time);
432   DCHECK_LE(timing.first_response_start_time, timing.final_response_start_time);
433   DCHECK_LE(timing.first_loader_callback_time,
434             timing.final_loader_callback_time);
435 
436   ukm::builders::NavigationTiming builder(GetDelegate().GetPageUkmSourceId());
437 
438   // Record the elapsed time from the navigation start milestone.
439   builder
440       .SetFirstRequestStart(
441           (timing.first_request_start_time - navigation_start_time)
442               .InMilliseconds())
443       .SetFirstResponseStart(
444           (timing.first_response_start_time - navigation_start_time)
445               .InMilliseconds())
446       .SetFirstLoaderCallback(
447           (timing.first_loader_callback_time - navigation_start_time)
448               .InMilliseconds())
449       .SetFinalRequestStart(
450           (timing.final_request_start_time - navigation_start_time)
451               .InMilliseconds())
452       .SetFinalResponseStart(
453           (timing.final_response_start_time - navigation_start_time)
454               .InMilliseconds())
455       .SetFinalLoaderCallback(
456           (timing.final_loader_callback_time - navigation_start_time)
457               .InMilliseconds())
458       .SetNavigationCommitSent(
459           (timing.navigation_commit_sent_time - navigation_start_time)
460               .InMilliseconds());
461 
462   // Record the elapsed time from the navigation start milestone for the 103
463   // Early Hints experiment (https://crbug.com/1093693). Note that multiple 103
464   // responses can be served per request. These metrics use the first 103
465   // response as the timing.
466   if (!timing.early_hints_for_first_request_time.is_null()) {
467     builder.SetEarlyHintsForFirstRequest(
468         (timing.early_hints_for_first_request_time - navigation_start_time)
469             .InMilliseconds());
470   }
471   if (!timing.early_hints_for_final_request_time.is_null()) {
472     builder.SetEarlyHintsForFinalRequest(
473         (timing.early_hints_for_final_request_time - navigation_start_time)
474             .InMilliseconds());
475   }
476 
477   builder.Record(ukm::UkmRecorder::Get());
478 }
479 
RecordTimingMetrics(const page_load_metrics::mojom::PageLoadTiming & timing)480 void UkmPageLoadMetricsObserver::RecordTimingMetrics(
481     const page_load_metrics::mojom::PageLoadTiming& timing) {
482   ukm::builders::PageLoad builder(GetDelegate().GetPageUkmSourceId());
483 
484   base::Optional<int64_t> rounded_site_engagement_score =
485       GetRoundedSiteEngagementScore();
486   if (rounded_site_engagement_score) {
487     builder.SetSiteEngagementScore(rounded_site_engagement_score.value());
488   }
489 
490   base::Optional<bool> third_party_cookie_blocking_enabled =
491       GetThirdPartyCookieBlockingEnabled();
492   if (third_party_cookie_blocking_enabled) {
493     builder.SetThirdPartyCookieBlockingEnabledForSite(
494         third_party_cookie_blocking_enabled.value());
495     UMA_HISTOGRAM_BOOLEAN("Privacy.ThirdPartyCookieBlockingEnabledForSite",
496                           third_party_cookie_blocking_enabled.value());
497   }
498 
499   if (timing.input_to_navigation_start) {
500     builder.SetExperimental_InputToNavigationStart(
501         timing.input_to_navigation_start.value().InMilliseconds());
502   }
503   if (timing.parse_timing->parse_start) {
504     builder.SetParseTiming_NavigationToParseStart(
505         timing.parse_timing->parse_start.value().InMilliseconds());
506   }
507   if (timing.document_timing->dom_content_loaded_event_start) {
508     builder.SetDocumentTiming_NavigationToDOMContentLoadedEventFired(
509         timing.document_timing->dom_content_loaded_event_start.value()
510             .InMilliseconds());
511   }
512   if (timing.document_timing->load_event_start) {
513     builder.SetDocumentTiming_NavigationToLoadEventFired(
514         timing.document_timing->load_event_start.value().InMilliseconds());
515   }
516   if (timing.paint_timing->first_paint) {
517     builder.SetPaintTiming_NavigationToFirstPaint(
518         timing.paint_timing->first_paint.value().InMilliseconds());
519   }
520   if (timing.paint_timing->first_contentful_paint) {
521     builder.SetPaintTiming_NavigationToFirstContentfulPaint(
522         timing.paint_timing->first_contentful_paint.value().InMilliseconds());
523   }
524   if (timing.paint_timing->first_meaningful_paint) {
525     builder.SetExperimental_PaintTiming_NavigationToFirstMeaningfulPaint(
526         timing.paint_timing->first_meaningful_paint.value().InMilliseconds());
527   }
528   const page_load_metrics::ContentfulPaintTimingInfo&
529       main_frame_largest_contentful_paint =
530           GetDelegate()
531               .GetLargestContentfulPaintHandler()
532               .MainFrameLargestContentfulPaint();
533   if (main_frame_largest_contentful_paint.ContainsValidTime() &&
534       WasStartedInForegroundOptionalEventInForeground(
535           main_frame_largest_contentful_paint.Time(), GetDelegate())) {
536     builder.SetPaintTiming_NavigationToLargestContentfulPaint2_MainFrame(
537         main_frame_largest_contentful_paint.Time().value().InMilliseconds());
538   }
539   const page_load_metrics::ContentfulPaintTimingInfo&
540       all_frames_largest_contentful_paint =
541           GetDelegate()
542               .GetLargestContentfulPaintHandler()
543               .MergeMainFrameAndSubframes();
544   if (all_frames_largest_contentful_paint.ContainsValidTime() &&
545       WasStartedInForegroundOptionalEventInForeground(
546           all_frames_largest_contentful_paint.Time(), GetDelegate())) {
547     builder.SetPaintTiming_NavigationToLargestContentfulPaint2(
548         all_frames_largest_contentful_paint.Time().value().InMilliseconds());
549   }
550   // TODO(crbug.com/1045640): Stop reporting the experimental obsolete versions.
551   const page_load_metrics::ContentfulPaintTimingInfo&
552       main_frame_experimental_largest_contentful_paint =
553           GetDelegate()
554               .GetExperimentalLargestContentfulPaintHandler()
555               .MainFrameLargestContentfulPaint();
556   if (main_frame_experimental_largest_contentful_paint.ContainsValidTime() &&
557       WasStartedInForegroundOptionalEventInForeground(
558           main_frame_experimental_largest_contentful_paint.Time(),
559           GetDelegate())) {
560     builder.SetPaintTiming_NavigationToLargestContentfulPaint_MainFrame(
561         main_frame_experimental_largest_contentful_paint.Time()
562             .value()
563             .InMilliseconds());
564   }
565   const page_load_metrics::ContentfulPaintTimingInfo&
566       all_frames_experimental_largest_contentful_paint =
567           GetDelegate()
568               .GetExperimentalLargestContentfulPaintHandler()
569               .MergeMainFrameAndSubframes();
570   if (all_frames_experimental_largest_contentful_paint.ContainsValidTime() &&
571       WasStartedInForegroundOptionalEventInForeground(
572           all_frames_experimental_largest_contentful_paint.Time(),
573           GetDelegate())) {
574     builder.SetPaintTiming_NavigationToLargestContentfulPaint(
575         all_frames_experimental_largest_contentful_paint.Time()
576             .value()
577             .InMilliseconds());
578   }
579   RecordInternalTimingMetrics(all_frames_largest_contentful_paint,
580                               all_frames_experimental_largest_contentful_paint);
581   if (timing.interactive_timing->first_input_delay) {
582     base::TimeDelta first_input_delay =
583         timing.interactive_timing->first_input_delay.value();
584     builder.SetInteractiveTiming_FirstInputDelay4(
585         first_input_delay.InMilliseconds());
586   }
587   if (timing.interactive_timing->first_input_timestamp) {
588     base::TimeDelta first_input_timestamp =
589         timing.interactive_timing->first_input_timestamp.value();
590     builder.SetInteractiveTiming_FirstInputTimestamp4(
591         first_input_timestamp.InMilliseconds());
592   }
593 
594   if (timing.interactive_timing->longest_input_delay) {
595     base::TimeDelta longest_input_delay =
596         timing.interactive_timing->longest_input_delay.value();
597     builder.SetInteractiveTiming_LongestInputDelay4(
598         longest_input_delay.InMilliseconds());
599   }
600   if (timing.interactive_timing->longest_input_timestamp) {
601     base::TimeDelta longest_input_timestamp =
602         timing.interactive_timing->longest_input_timestamp.value();
603     builder.SetInteractiveTiming_LongestInputTimestamp4(
604         longest_input_timestamp.InMilliseconds());
605   }
606   if (timing.interactive_timing->first_scroll_delay) {
607     base::TimeDelta first_scroll_delay =
608         timing.interactive_timing->first_scroll_delay.value();
609     builder.SetInteractiveTiming_FirstScrollDelay(
610         first_scroll_delay.InMilliseconds());
611   }
612   if (timing.interactive_timing->first_input_processing_time) {
613     base::TimeDelta first_input_processing_time =
614         timing.interactive_timing->first_input_processing_time.value();
615     builder.SetInteractiveTiming_FirstInputProcessingTimes(
616         first_input_processing_time.InMilliseconds());
617   }
618   builder.SetCpuTime(total_foreground_cpu_time_.InMilliseconds());
619 
620   // Use a bucket spacing factor of 1.3 for bytes.
621   builder.SetNet_CacheBytes2(ukm::GetExponentialBucketMin(cache_bytes_, 1.3));
622   builder.SetNet_NetworkBytes2(
623       ukm::GetExponentialBucketMin(network_bytes_, 1.3));
624 
625   // Use a bucket spacing factor of 10 for JS bytes.
626   builder.SetNet_JavaScriptBytes(
627       ukm::GetExponentialBucketMin(js_decoded_bytes_, 10));
628   builder.SetNet_JavaScriptMaxBytes(
629       ukm::GetExponentialBucketMin(js_max_decoded_bytes_, 10));
630 
631   builder.SetNet_ImageBytes(
632       ukm::GetExponentialBucketMin(image_total_bytes_, 1.15));
633   builder.SetNet_ImageSubframeBytes(
634       ukm::GetExponentialBucketMin(image_subframe_bytes_, 1.15));
635   builder.SetNet_MediaBytes(ukm::GetExponentialBucketMin(media_bytes_, 1.15));
636 
637   if (main_frame_timing_)
638     ReportMainResourceTimingMetrics(timing, &builder);
639 
640   builder.Record(ukm::UkmRecorder::Get());
641 }
642 
RecordInternalTimingMetrics(const page_load_metrics::ContentfulPaintTimingInfo & all_frames_largest_contentful_paint,const page_load_metrics::ContentfulPaintTimingInfo & all_frames_experimental_largest_contentful_paint)643 void UkmPageLoadMetricsObserver::RecordInternalTimingMetrics(
644     const page_load_metrics::ContentfulPaintTimingInfo&
645         all_frames_largest_contentful_paint,
646     const page_load_metrics::ContentfulPaintTimingInfo&
647         all_frames_experimental_largest_contentful_paint) {
648   ukm::builders::PageLoad_Internal debug_builder(
649       GetDelegate().GetPageUkmSourceId());
650   LargestContentState lcp_state = LargestContentState::kNotFound;
651   if (all_frames_largest_contentful_paint.ContainsValidTime()) {
652     if (WasStartedInForegroundOptionalEventInForeground(
653             all_frames_largest_contentful_paint.Time(), GetDelegate())) {
654       debug_builder.SetPaintTiming_LargestContentfulPaint_ContentType(
655           static_cast<int>(all_frames_largest_contentful_paint.Type()));
656       lcp_state = LargestContentState::kReported;
657     } else {
658       // TODO(npm): figure out why this code can be reached given that
659       // RecordTimingMetrics() is only called when was_hidden_ is set to false.
660       lcp_state = LargestContentState::kFoundButNotReported;
661     }
662   } else if (all_frames_largest_contentful_paint.Time().has_value()) {
663     DCHECK(all_frames_largest_contentful_paint.Size());
664     lcp_state = LargestContentState::kLargestImageLoading;
665   } else {
666     DCHECK(all_frames_largest_contentful_paint.Empty());
667     lcp_state = LargestContentState::kNotFound;
668   }
669   debug_builder.SetPaintTiming_LargestContentfulPaint_TerminationState(
670       static_cast<int>(lcp_state));
671 
672   LargestContentState experimental_lcp_state = LargestContentState::kNotFound;
673   if (all_frames_experimental_largest_contentful_paint.ContainsValidTime()) {
674     if (WasStartedInForegroundOptionalEventInForeground(
675             all_frames_experimental_largest_contentful_paint.Time(),
676             GetDelegate())) {
677       debug_builder
678           .SetPaintTiming_ExperimentalLargestContentfulPaint_ContentType(
679               static_cast<int>(
680                   all_frames_experimental_largest_contentful_paint.Type()));
681       experimental_lcp_state = LargestContentState::kReported;
682     } else {
683       // TODO(npm): figure out why this code can be reached given that
684       // RecordTimingMetrics() is only called when was_hidden_ is set to false.
685       experimental_lcp_state = LargestContentState::kFoundButNotReported;
686     }
687   } else if (all_frames_experimental_largest_contentful_paint.Time()
688                  .has_value()) {
689     DCHECK(all_frames_experimental_largest_contentful_paint.Size());
690     experimental_lcp_state = LargestContentState::kLargestImageLoading;
691   } else {
692     DCHECK(all_frames_experimental_largest_contentful_paint.Empty());
693     experimental_lcp_state = LargestContentState::kNotFound;
694   }
695   debug_builder
696       .SetPaintTiming_ExperimentalLargestContentfulPaint_TerminationState(
697           static_cast<int>(lcp_state));
698   debug_builder.Record(ukm::UkmRecorder::Get());
699 }
700 
RecordPageLoadMetrics(base::TimeTicks app_background_time)701 void UkmPageLoadMetricsObserver::RecordPageLoadMetrics(
702     base::TimeTicks app_background_time) {
703   ukm::builders::PageLoad builder(GetDelegate().GetPageUkmSourceId());
704   base::Optional<base::TimeDelta> foreground_duration =
705       page_load_metrics::GetInitialForegroundDuration(GetDelegate(),
706                                                       app_background_time);
707   if (foreground_duration) {
708     builder.SetPageTiming_ForegroundDuration(
709         foreground_duration.value().InMilliseconds());
710   }
711 
712   builder.SetSiteInstanceRenderProcessAssignment(
713       SiteInstanceRenderProcessAssignmentToInt(render_process_assignment_));
714 
715   // Convert to the EffectiveConnectionType as used in SystemProfileProto
716   // before persisting the metric.
717   metrics::SystemProfileProto::Network::EffectiveConnectionType
718       proto_effective_connection_type =
719           metrics::ConvertEffectiveConnectionType(effective_connection_type_);
720   if (proto_effective_connection_type !=
721       metrics::SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
722     builder.SetNet_EffectiveConnectionType2_OnNavigationStart(
723         static_cast<int64_t>(proto_effective_connection_type));
724   }
725 
726   if (http_response_code_) {
727     builder.SetNet_HttpResponseCode(
728         static_cast<int64_t>(http_response_code_.value()));
729   }
730   if (http_rtt_estimate_) {
731     builder.SetNet_HttpRttEstimate_OnNavigationStart(
732         static_cast<int64_t>(http_rtt_estimate_.value().InMilliseconds()));
733   }
734   if (transport_rtt_estimate_) {
735     builder.SetNet_TransportRttEstimate_OnNavigationStart(
736         static_cast<int64_t>(transport_rtt_estimate_.value().InMilliseconds()));
737   }
738   if (downstream_kbps_estimate_) {
739     builder.SetNet_DownstreamKbpsEstimate_OnNavigationStart(
740         static_cast<int64_t>(downstream_kbps_estimate_.value()));
741   }
742   if (GetDelegate().DidCommit() && was_cached_) {
743     builder.SetWasCached(1);
744   }
745   if (GetDelegate().DidCommit() && navigation_is_cross_process_) {
746     builder.SetIsCrossProcessNavigation(navigation_is_cross_process_);
747   }
748   if (GetDelegate().DidCommit()) {
749     builder.SetNavigationEntryOffset(navigation_entry_offset_);
750     builder.SetMainDocumentSequenceNumber(main_document_sequence_number_);
751   }
752   builder.Record(ukm::UkmRecorder::Get());
753 }
754 
RecordRendererUsageMetrics()755 void UkmPageLoadMetricsObserver::RecordRendererUsageMetrics() {
756   ukm::builders::PageLoad builder(GetDelegate().GetPageUkmSourceId());
757 
758   builder.SetSiteInstanceRenderProcessAssignment(
759       SiteInstanceRenderProcessAssignmentToInt(render_process_assignment_));
760 
761   builder.Record(ukm::UkmRecorder::Get());
762 }
763 
ReportMainResourceTimingMetrics(const page_load_metrics::mojom::PageLoadTiming & timing,ukm::builders::PageLoad * builder)764 void UkmPageLoadMetricsObserver::ReportMainResourceTimingMetrics(
765     const page_load_metrics::mojom::PageLoadTiming& timing,
766     ukm::builders::PageLoad* builder) {
767   DCHECK(main_frame_timing_.has_value());
768 
769   builder->SetMainFrameResource_SocketReused(main_frame_timing_->socket_reused);
770 
771   int64_t dns_start_ms =
772       main_frame_timing_->connect_timing.dns_start.since_origin()
773           .InMilliseconds();
774   int64_t dns_end_ms = main_frame_timing_->connect_timing.dns_end.since_origin()
775                            .InMilliseconds();
776   int64_t connect_start_ms =
777       main_frame_timing_->connect_timing.connect_start.since_origin()
778           .InMilliseconds();
779   int64_t connect_end_ms =
780       main_frame_timing_->connect_timing.connect_end.since_origin()
781           .InMilliseconds();
782   int64_t request_start_ms =
783       main_frame_timing_->request_start.since_origin().InMilliseconds();
784   int64_t send_start_ms =
785       main_frame_timing_->send_start.since_origin().InMilliseconds();
786   int64_t receive_headers_end_ms =
787       main_frame_timing_->receive_headers_end.since_origin().InMilliseconds();
788 
789   DCHECK_LE(dns_start_ms, dns_end_ms);
790   DCHECK_LE(dns_end_ms, connect_start_ms);
791   DCHECK_LE(dns_start_ms, connect_start_ms);
792   DCHECK_LE(connect_start_ms, connect_end_ms);
793 
794   int64_t dns_duration_ms = dns_end_ms - dns_start_ms;
795   int64_t connect_duration_ms = connect_end_ms - connect_start_ms;
796   int64_t request_start_to_send_start_ms = send_start_ms - request_start_ms;
797   int64_t send_start_to_receive_headers_end_ms =
798       receive_headers_end_ms - send_start_ms;
799   int64_t request_start_to_receive_headers_end_ms =
800       receive_headers_end_ms - request_start_ms;
801 
802   builder->SetMainFrameResource_DNSDelay(dns_duration_ms);
803   builder->SetMainFrameResource_ConnectDelay(connect_duration_ms);
804   if (request_start_to_send_start_ms >= 0) {
805     builder->SetMainFrameResource_RequestStartToSendStart(
806         request_start_to_send_start_ms);
807   }
808   if (send_start_to_receive_headers_end_ms >= 0) {
809     builder->SetMainFrameResource_SendStartToReceiveHeadersEnd(
810         send_start_to_receive_headers_end_ms);
811   }
812   builder->SetMainFrameResource_RequestStartToReceiveHeadersEnd(
813       request_start_to_receive_headers_end_ms);
814 
815   if (!main_frame_timing_->request_start.is_null() &&
816       !GetDelegate().GetNavigationStart().is_null()) {
817     base::TimeDelta navigation_start_to_request_start =
818         main_frame_timing_->request_start - GetDelegate().GetNavigationStart();
819 
820     builder->SetMainFrameResource_NavigationStartToRequestStart(
821         navigation_start_to_request_start.InMilliseconds());
822   }
823 
824   if (!main_frame_timing_->receive_headers_start.is_null() &&
825       !GetDelegate().GetNavigationStart().is_null()) {
826     base::TimeDelta navigation_start_to_receive_headers_start =
827         main_frame_timing_->receive_headers_start -
828         GetDelegate().GetNavigationStart();
829     builder->SetMainFrameResource_NavigationStartToReceiveHeadersStart(
830         navigation_start_to_receive_headers_start.InMilliseconds());
831   }
832 
833   if (connection_info_.has_value()) {
834     page_load_metrics::NetworkProtocol protocol =
835         page_load_metrics::GetNetworkProtocol(*connection_info_);
836     if (IsSupportedProtocol(protocol)) {
837       builder->SetMainFrameResource_HttpProtocolScheme(
838           static_cast<int>(protocol));
839     }
840   }
841 
842   if (main_frame_request_redirect_count_ > 0) {
843     builder->SetMainFrameResource_RedirectCount(
844         main_frame_request_redirect_count_);
845   }
846 }
847 
ReportLayoutStability()848 void UkmPageLoadMetricsObserver::ReportLayoutStability() {
849   ukm::builders::PageLoad builder(GetDelegate().GetPageUkmSourceId());
850   builder
851       .SetLayoutInstability_CumulativeShiftScore(
852           page_load_metrics::LayoutShiftUkmValue(
853               GetDelegate().GetPageRenderData().layout_shift_score))
854       .SetLayoutInstability_CumulativeShiftScore_BeforeInputOrScroll(
855           page_load_metrics::LayoutShiftUkmValue(
856               GetDelegate()
857                   .GetPageRenderData()
858                   .layout_shift_score_before_input_or_scroll))
859       .SetLayoutInstability_CumulativeShiftScore_MainFrame(
860           page_load_metrics::LayoutShiftUkmValue(
861               GetDelegate().GetMainFrameRenderData().layout_shift_score))
862       .SetLayoutInstability_CumulativeShiftScore_MainFrame_BeforeInputOrScroll(
863           page_load_metrics::LayoutShiftUkmValue(
864               GetDelegate()
865                   .GetMainFrameRenderData()
866                   .layout_shift_score_before_input_or_scroll));
867   // Record CLS normalization UKM.
868   if (base::FeatureList::IsEnabled(kLayoutShiftNormalizationRecordUKM) &&
869       !GetDelegate().GetNormalizedCLSData().data_tainted) {
870     builder
871         .SetLayoutInstability_MaxCumulativeShiftScore_SessionWindow_Gap1000ms(
872             page_load_metrics::LayoutShiftUkmValue(
873                 GetDelegate()
874                     .GetNormalizedCLSData()
875                     .session_windows_gap1000ms_maxMax_max_cls))
876         .SetLayoutInstability_MaxCumulativeShiftScore_SessionWindow_Gap1000ms_Max5000ms(
877             page_load_metrics::LayoutShiftUkmValue(
878                 GetDelegate()
879                     .GetNormalizedCLSData()
880                     .session_windows_gap1000ms_max5000ms_max_cls))
881         .SetLayoutInstability_MaxCumulativeShiftScore_SlidingWindow_Duration1000ms(
882             page_load_metrics::LayoutShiftUkmValue(
883                 GetDelegate()
884                     .GetNormalizedCLSData()
885                     .sliding_windows_duration1000ms_max_cls))
886         .SetLayoutInstability_MaxCumulativeShiftScore_SlidingWindow_Duration300ms(
887             page_load_metrics::LayoutShiftUkmValue(
888                 GetDelegate()
889                     .GetNormalizedCLSData()
890                     .sliding_windows_duration300ms_max_cls))
891         .SetLayoutInstability_AverageCumulativeShiftScore_SessionWindow_Gap5000ms(
892             page_load_metrics::LayoutShiftUkmValue(
893                 GetDelegate()
894                     .GetNormalizedCLSData()
895                     .session_windows_gap5000ms_maxMax_average_cls));
896   }
897   builder.Record(ukm::UkmRecorder::Get());
898 
899   // TODO(crbug.com/1064483): We should move UMA recording to components/
900 
901   UMA_HISTOGRAM_COUNTS_100(
902       "PageLoad.LayoutInstability.CumulativeShiftScore",
903       page_load_metrics::LayoutShiftUmaValue(
904           GetDelegate().GetPageRenderData().layout_shift_score));
905 
906   TRACE_EVENT_INSTANT1("loading", "CumulativeShiftScore::AllFrames::UMA",
907                        TRACE_EVENT_SCOPE_THREAD, "data",
908                        CumulativeShiftScoreTraceData(
909                            GetDelegate().GetPageRenderData().layout_shift_score,
910                            GetDelegate()
911                                .GetPageRenderData()
912                                .layout_shift_score_before_input_or_scroll));
913 
914   UMA_HISTOGRAM_COUNTS_100(
915       "PageLoad.LayoutInstability.CumulativeShiftScore.MainFrame",
916       page_load_metrics::LayoutShiftUmaValue(
917           GetDelegate().GetMainFrameRenderData().layout_shift_score));
918 }
919 
ReportPerfectHeuristicsMetrics()920 void UkmPageLoadMetricsObserver::ReportPerfectHeuristicsMetrics() {
921   if (!delay_async_script_execution_before_finished_parsing_seen_ &&
922       !delay_competing_low_priority_requests_seen_) {
923     return;
924   }
925 
926   ukm::builders::PerfectHeuristics builder(GetDelegate().GetPageUkmSourceId());
927   if (delay_async_script_execution_before_finished_parsing_seen_)
928     builder.Setdelay_async_script_execution_before_finished_parsing(1);
929   if (delay_competing_low_priority_requests_seen_)
930     builder.SetDelayCompetingLowPriorityRequests(1);
931   builder.Record(ukm::UkmRecorder::Get());
932 }
933 
RecordAbortMetrics(const page_load_metrics::mojom::PageLoadTiming & timing,base::TimeTicks page_end_time,ukm::builders::PageLoad * builder)934 void UkmPageLoadMetricsObserver::RecordAbortMetrics(
935     const page_load_metrics::mojom::PageLoadTiming& timing,
936     base::TimeTicks page_end_time,
937     ukm::builders::PageLoad* builder) {
938   PageLoadType page_load_type = PageLoadType::kNeverForegrounded;
939   if (page_load_metrics::WasInForeground(GetDelegate())) {
940     page_load_type = timing.paint_timing->first_contentful_paint.has_value()
941                          ? PageLoadType::kReachedFCP
942                          : PageLoadType::kAborted;
943   }
944   if (currently_in_foreground_ && !last_time_shown_.is_null()) {
945     total_foreground_duration_ += page_end_time - last_time_shown_;
946   }
947   UMA_HISTOGRAM_ENUMERATION("PageLoad.Experimental.PageLoadType",
948                             page_load_type);
949   PAGE_LOAD_LONG_HISTOGRAM("PageLoad.Experimental.TotalForegroundDuration",
950                            total_foreground_duration_);
951 
952   builder->SetExperimental_PageLoadType(static_cast<int>(page_load_type))
953       .SetExperimental_TotalForegroundDuration(
954           ukm::GetExponentialBucketMinForUserTiming(
955               total_foreground_duration_.InMilliseconds()));
956 }
957 
RecordInputTimingMetrics()958 void UkmPageLoadMetricsObserver::RecordInputTimingMetrics() {
959   ukm::builders::PageLoad(GetDelegate().GetPageUkmSourceId())
960       .SetInteractiveTiming_NumInputEvents(
961           GetDelegate().GetPageInputTiming().num_input_events)
962       .SetInteractiveTiming_TotalInputDelay(
963           GetDelegate().GetPageInputTiming().total_input_delay.InMilliseconds())
964       .SetInteractiveTiming_TotalAdjustedInputDelay(
965           GetDelegate()
966               .GetPageInputTiming()
967               .total_adjusted_input_delay.InMilliseconds())
968       .Record(ukm::UkmRecorder::Get());
969 }
970 
RecordSmoothnessMetrics()971 void UkmPageLoadMetricsObserver::RecordSmoothnessMetrics() {
972   auto* smoothness =
973       ukm_smoothness_data_.GetMemoryAs<cc::UkmSmoothnessDataShared>();
974   if (!smoothness) {
975     return;
976   }
977 
978   base::ElapsedTimer timer;
979   const uint32_t kMaxRetries = 5;
980   uint32_t retries = 0;
981   cc::UkmSmoothnessData smoothness_data;
982   base::subtle::Atomic32 version;
983   do {
984     const uint32_t kMaxReadAttempts = 32;
985     version = smoothness->seq_lock.ReadBegin(kMaxReadAttempts);
986     device::OneWriterSeqLock::AtomicReaderMemcpy(
987         &smoothness_data, &smoothness->data, sizeof(cc::UkmSmoothnessData));
988   } while (smoothness->seq_lock.ReadRetry(version) && ++retries < kMaxRetries);
989 
990   UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
991       "Graphics.Smoothness.Diagnostic.ReadSharedMemoryDuration",
992       timer.Elapsed(), base::TimeDelta::FromMicroseconds(1),
993       base::TimeDelta::FromMilliseconds(5), 100);
994   UMA_HISTOGRAM_BOOLEAN(
995       "Graphics.Smoothness.Diagnostic.ReadSharedMemoryUKMSuccess",
996       retries < kMaxRetries);
997 
998   if (retries >= kMaxRetries)
999     return;
1000   ukm::builders::Graphics_Smoothness_NormalizedPercentDroppedFrames(
1001       GetDelegate().GetPageUkmSourceId())
1002       .SetAverage(smoothness_data.avg_smoothness)
1003       .SetPercentile95(smoothness_data.percentile_95)
1004       .SetAboveThreshold(smoothness_data.above_threshold)
1005       .SetWorstCase(smoothness_data.worst_smoothness)
1006       .Record(ukm::UkmRecorder::Get());
1007 }
1008 
RecordMobileFriendlinessMetrics()1009 void UkmPageLoadMetricsObserver::RecordMobileFriendlinessMetrics() {
1010   ukm::builders::MobileFriendliness mf(GetDelegate().GetPageUkmSourceId());
1011   mf.SetViewportDeviceWidth(
1012         GetDelegate().GetMobileFriendliness().viewport_device_width)
1013       .SetAllowUserZoom(GetDelegate().GetMobileFriendliness().allow_user_zoom);
1014   const int initial_scale_x10 = std::floor(
1015       GetDelegate().GetMobileFriendliness().viewport_initial_scale * 10);
1016   if (initial_scale_x10 > 0) {
1017     mf.SetViewportInitialScaleX10(
1018         ukm::GetExponentialBucketMin(initial_scale_x10, 1.2));
1019   }
1020 
1021   const int hardcoded_width =
1022       GetDelegate().GetMobileFriendliness().viewport_hardcoded_width;
1023   if (hardcoded_width > 0) {
1024     mf.SetViewportHardcodedWidth(
1025         BucketWithOffsetAndUnit(hardcoded_width, 500, 10));
1026   }
1027   mf.Record(ukm::UkmRecorder::Get());
1028 }
1029 
RecordPageEndMetrics(const page_load_metrics::mojom::PageLoadTiming * timing,base::TimeTicks page_end_time)1030 void UkmPageLoadMetricsObserver::RecordPageEndMetrics(
1031     const page_load_metrics::mojom::PageLoadTiming* timing,
1032     base::TimeTicks page_end_time) {
1033   ukm::builders::PageLoad builder(GetDelegate().GetPageUkmSourceId());
1034   // page_transition_ fits in a uint32_t, so we can safely cast to int64_t.
1035   builder.SetNavigation_PageTransition(static_cast<int64_t>(page_transition_));
1036 
1037   // GetDelegate().GetPageEndReason() fits in a uint32_t, so we can safely cast
1038   // to int64_t.
1039   int64_t page_end_reason = GetDelegate().GetPageEndReason();
1040   if (page_end_reason == page_load_metrics::PageEndReason::END_NONE &&
1041       was_hidden_) {
1042     page_end_reason = page_load_metrics::PageEndReason::END_HIDDEN;
1043   }
1044   builder.SetNavigation_PageEndReason2(page_end_reason);
1045   bool is_user_initiated_navigation =
1046       // All browser initiated page loads are user-initiated.
1047       GetDelegate().GetUserInitiatedInfo().browser_initiated ||
1048       // Renderer-initiated navigations are user-initiated if there is an
1049       // associated input event.
1050       GetDelegate().GetUserInitiatedInfo().user_input_event;
1051   builder.SetExperimental_Navigation_UserInitiated(
1052       is_user_initiated_navigation);
1053   if (timing)
1054     RecordAbortMetrics(*timing, page_end_time, &builder);
1055 
1056   builder.Record(ukm::UkmRecorder::Get());
1057 }
1058 
1059 base::Optional<int64_t>
GetRoundedSiteEngagementScore() const1060 UkmPageLoadMetricsObserver::GetRoundedSiteEngagementScore() const {
1061   if (!browser_context_)
1062     return base::nullopt;
1063 
1064   Profile* profile = Profile::FromBrowserContext(browser_context_);
1065   SiteEngagementService* engagement_service =
1066       SiteEngagementService::Get(profile);
1067 
1068   // UKM privacy requires the engagement score be rounded to nearest
1069   // value of 10.
1070   int64_t rounded_document_engagement_score =
1071       static_cast<int>(std::roundf(
1072           engagement_service->GetScore(GetDelegate().GetUrl()) / 10.0)) *
1073       10;
1074 
1075   DCHECK(rounded_document_engagement_score >= 0 &&
1076          rounded_document_engagement_score <=
1077              engagement_service->GetMaxPoints());
1078 
1079   return rounded_document_engagement_score;
1080 }
1081 
1082 base::Optional<bool>
GetThirdPartyCookieBlockingEnabled() const1083 UkmPageLoadMetricsObserver::GetThirdPartyCookieBlockingEnabled() const {
1084   if (!browser_context_)
1085     return base::nullopt;
1086 
1087   Profile* profile = Profile::FromBrowserContext(browser_context_);
1088   auto cookie_settings = CookieSettingsFactory::GetForProfile(profile);
1089   if (!cookie_settings->ShouldBlockThirdPartyCookies())
1090     return base::nullopt;
1091 
1092   return !cookie_settings->IsThirdPartyAccessAllowed(GetDelegate().GetUrl(),
1093                                                      nullptr /* source */);
1094 }
1095 
OnTimingUpdate(content::RenderFrameHost * subframe_rfh,const page_load_metrics::mojom::PageLoadTiming & timing)1096 void UkmPageLoadMetricsObserver::OnTimingUpdate(
1097     content::RenderFrameHost* subframe_rfh,
1098     const page_load_metrics::mojom::PageLoadTiming& timing) {
1099   bool loading_enabled;
1100   TRACE_EVENT_CATEGORY_GROUP_ENABLED("loading", &loading_enabled);
1101   if (!loading_enabled)
1102     return;
1103   const page_load_metrics::ContentfulPaintTimingInfo& paint =
1104       GetDelegate()
1105           .GetLargestContentfulPaintHandler()
1106           .MergeMainFrameAndSubframes();
1107 
1108   if (paint.ContainsValidTime()) {
1109     TRACE_EVENT_INSTANT2(
1110         "loading",
1111         "NavStartToLargestContentfulPaint::Candidate::AllFrames::UKM",
1112         TRACE_EVENT_SCOPE_THREAD, "data", paint.DataAsTraceValue(),
1113         "main_frame_tree_node_id",
1114         GetDelegate().GetLargestContentfulPaintHandler().MainFrameTreeNodeId());
1115   } else {
1116     TRACE_EVENT_INSTANT1(
1117         "loading",
1118         "NavStartToLargestContentfulPaint::"
1119         "Invalidate::AllFrames::UKM",
1120         TRACE_EVENT_SCOPE_THREAD, "main_frame_tree_node_id",
1121         GetDelegate().GetLargestContentfulPaintHandler().MainFrameTreeNodeId());
1122   }
1123 
1124   const page_load_metrics::ContentfulPaintTimingInfo&
1125       experimental_largest_contentful_paint =
1126           GetDelegate()
1127               .GetExperimentalLargestContentfulPaintHandler()
1128               .MergeMainFrameAndSubframes();
1129   if (experimental_largest_contentful_paint.ContainsValidTime()) {
1130     TRACE_EVENT_INSTANT2(
1131         "loading",
1132         "NavStartToExperimentalLargestContentfulPaint::Candidate::AllFrames::"
1133         "UKM",
1134         TRACE_EVENT_SCOPE_THREAD, "data",
1135         experimental_largest_contentful_paint.DataAsTraceValue(),
1136         "main_frame_tree_node_id",
1137         GetDelegate()
1138             .GetExperimentalLargestContentfulPaintHandler()
1139             .MainFrameTreeNodeId());
1140   } else {
1141     TRACE_EVENT_INSTANT1("loading",
1142                          "NavStartToExperimentalLargestContentfulPaint::"
1143                          "Invalidate::AllFrames::UKM",
1144                          TRACE_EVENT_SCOPE_THREAD, "main_frame_tree_node_id",
1145                          GetDelegate()
1146                              .GetExperimentalLargestContentfulPaintHandler()
1147                              .MainFrameTreeNodeId());
1148   }
1149 }
1150 
SetUpSharedMemoryForSmoothness(const base::ReadOnlySharedMemoryRegion & shared_memory)1151 void UkmPageLoadMetricsObserver::SetUpSharedMemoryForSmoothness(
1152     const base::ReadOnlySharedMemoryRegion& shared_memory) {
1153   ukm_smoothness_data_ = shared_memory.Map();
1154 }
1155 
OnCpuTimingUpdate(content::RenderFrameHost * subframe_rfh,const page_load_metrics::mojom::CpuTiming & timing)1156 void UkmPageLoadMetricsObserver::OnCpuTimingUpdate(
1157     content::RenderFrameHost* subframe_rfh,
1158     const page_load_metrics::mojom::CpuTiming& timing) {
1159   if (GetDelegate().GetVisibilityTracker().currently_in_foreground())
1160     total_foreground_cpu_time_ += timing.task_time;
1161 }
1162 
DidActivatePortal(base::TimeTicks activation_time)1163 void UkmPageLoadMetricsObserver::DidActivatePortal(
1164     base::TimeTicks activation_time) {
1165   is_portal_ = false;
1166 }
1167 
RecordNoStatePrefetchMetrics(content::NavigationHandle * navigation_handle,ukm::SourceId source_id)1168 void UkmPageLoadMetricsObserver::RecordNoStatePrefetchMetrics(
1169     content::NavigationHandle* navigation_handle,
1170     ukm::SourceId source_id) {
1171   prerender::PrerenderManager* const prerender_manager =
1172       prerender::PrerenderManagerFactory::GetForBrowserContext(
1173           navigation_handle->GetWebContents()->GetBrowserContext());
1174   if (!prerender_manager)
1175     return;
1176 
1177   const std::vector<GURL>& redirects = navigation_handle->GetRedirectChain();
1178 
1179   base::TimeDelta prefetch_age;
1180   prerender::FinalStatus final_status;
1181   prerender::Origin prefetch_origin;
1182 
1183   bool nostate_prefetch_entry_found = prerender_manager->GetPrefetchInformation(
1184       navigation_handle->GetURL(), &prefetch_age, &final_status,
1185       &prefetch_origin);
1186 
1187   // Try the URLs from the redirect chain.
1188   if (!nostate_prefetch_entry_found) {
1189     for (const auto& url : redirects) {
1190       nostate_prefetch_entry_found = prerender_manager->GetPrefetchInformation(
1191           url, &prefetch_age, &final_status, &prefetch_origin);
1192       if (nostate_prefetch_entry_found)
1193         break;
1194     }
1195   }
1196 
1197   if (!nostate_prefetch_entry_found)
1198     return;
1199 
1200   ukm::builders::NoStatePrefetch builder(source_id);
1201   builder.SetPrefetchedRecently_PrefetchAge(
1202       ukm::GetExponentialBucketMinForUserTiming(prefetch_age.InMilliseconds()));
1203   builder.SetPrefetchedRecently_FinalStatus(final_status);
1204   builder.SetPrefetchedRecently_Origin(prefetch_origin);
1205   builder.Record(ukm::UkmRecorder::Get());
1206 }
1207 
IsOfflinePreview(content::WebContents * web_contents) const1208 bool UkmPageLoadMetricsObserver::IsOfflinePreview(
1209     content::WebContents* web_contents) const {
1210 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
1211   offline_pages::OfflinePageTabHelper* tab_helper =
1212       offline_pages::OfflinePageTabHelper::FromWebContents(web_contents);
1213   return tab_helper && tab_helper->GetOfflinePreviewItem();
1214 #else
1215   return false;
1216 #endif
1217 }
1218 
RecordGeneratedNavigationUKM(ukm::SourceId source_id,const GURL & committed_url)1219 void UkmPageLoadMetricsObserver::RecordGeneratedNavigationUKM(
1220     ukm::SourceId source_id,
1221     const GURL& committed_url) {
1222   bool final_url_is_home_page = IsUserHomePage(browser_context_, committed_url);
1223   bool final_url_is_default_search =
1224       IsDefaultSearchEngine(browser_context_, committed_url);
1225 
1226   if (!final_url_is_home_page && !final_url_is_default_search &&
1227       !start_url_is_home_page_ && !start_url_is_default_search_) {
1228     return;
1229   }
1230 
1231   ukm::builders::GeneratedNavigation builder(source_id);
1232   builder.SetFinalURLIsHomePage(final_url_is_home_page);
1233   builder.SetFinalURLIsDefaultSearchEngine(final_url_is_default_search);
1234   builder.SetFirstURLIsHomePage(start_url_is_home_page_);
1235   builder.SetFirstURLIsDefaultSearchEngine(start_url_is_default_search_);
1236   builder.Record(ukm::UkmRecorder::Get());
1237 }
1238 
OnLoadingBehaviorObserved(content::RenderFrameHost * rfh,int behavior_flag)1239 void UkmPageLoadMetricsObserver::OnLoadingBehaviorObserved(
1240     content::RenderFrameHost* rfh,
1241     int behavior_flag) {
1242   if (behavior_flag &
1243       blink::LoadingBehaviorFlag::
1244           kLoadingBehaviorAsyncScriptReadyBeforeDocumentFinishedParsing) {
1245     delay_async_script_execution_before_finished_parsing_seen_ = true;
1246   }
1247 
1248   if (behavior_flag & blink::LoadingBehaviorFlag::
1249                           kLoadingBehaviorCompetingLowPriorityRequestsDelayed) {
1250     delay_competing_low_priority_requests_seen_ = true;
1251   }
1252 }
1253