blob: 5c4ee45b6baf5c095aa7400b89fe508b96843270 [file] [log] [blame]
// Copyright 2015 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 "chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
#include "net/http/http_response_headers.h"
#include "ui/base/page_transition_types.h"
namespace {
// TODO(bmcquade): If other observers want to log histograms based on load type,
// promote this enum to page_load_metrics_observer.h.
enum PageLoadType {
LOAD_TYPE_NONE = 0,
LOAD_TYPE_RELOAD,
LOAD_TYPE_FORWARD_BACK,
LOAD_TYPE_NEW_NAVIGATION
};
PageLoadType GetPageLoadType(ui::PageTransition transition) {
if (transition & ui::PAGE_TRANSITION_FORWARD_BACK) {
return LOAD_TYPE_FORWARD_BACK;
}
if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD)) {
return LOAD_TYPE_RELOAD;
}
if (ui::PageTransitionIsNewNavigation(transition)) {
return LOAD_TYPE_NEW_NAVIGATION;
}
NOTREACHED() << "Received PageTransition with no matching PageLoadType.";
return LOAD_TYPE_NONE;
}
void RecordFirstMeaningfulPaintStatus(
internal::FirstMeaningfulPaintStatus status) {
UMA_HISTOGRAM_ENUMERATION(internal::kHistogramFirstMeaningfulPaintStatus,
status, internal::FIRST_MEANINGFUL_PAINT_LAST_ENTRY);
}
void RecordTimeToInteractiveStatus(internal::TimeToInteractiveStatus status) {
UMA_HISTOGRAM_ENUMERATION(internal::kHistogramTimeToInteractiveStatus, status,
internal::TIME_TO_INTERACTIVE_LAST_ENTRY);
}
} // namespace
namespace internal {
const char kHistogramDomContentLoaded[] =
"PageLoad.DocumentTiming.NavigationToDOMContentLoadedEventFired";
const char kBackgroundHistogramDomContentLoaded[] =
"PageLoad.DocumentTiming.NavigationToDOMContentLoadedEventFired.Background";
const char kHistogramLoad[] =
"PageLoad.DocumentTiming.NavigationToLoadEventFired";
const char kBackgroundHistogramLoad[] =
"PageLoad.DocumentTiming.NavigationToLoadEventFired.Background";
const char kHistogramFirstLayout[] =
"PageLoad.DocumentTiming.NavigationToFirstLayout";
const char kBackgroundHistogramFirstLayout[] =
"PageLoad.DocumentTiming.NavigationToFirstLayout.Background";
const char kHistogramFirstPaint[] =
"PageLoad.PaintTiming.NavigationToFirstPaint";
const char kBackgroundHistogramFirstPaint[] =
"PageLoad.PaintTiming.NavigationToFirstPaint.Background";
const char kHistogramFirstTextPaint[] =
"PageLoad.PaintTiming.NavigationToFirstTextPaint";
const char kBackgroundHistogramFirstTextPaint[] =
"PageLoad.PaintTiming.NavigationToFirstTextPaint.Background";
const char kHistogramFirstImagePaint[] =
"PageLoad.PaintTiming.NavigationToFirstImagePaint";
const char kBackgroundHistogramFirstImagePaint[] =
"PageLoad.PaintTiming.NavigationToFirstImagePaint.Background";
const char kHistogramFirstContentfulPaint[] =
"PageLoad.PaintTiming.NavigationToFirstContentfulPaint";
const char kBackgroundHistogramFirstContentfulPaint[] =
"PageLoad.PaintTiming.NavigationToFirstContentfulPaint.Background";
const char kHistogramFirstMeaningfulPaint[] =
"PageLoad.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint";
const char kHistogramTimeToInteractive[] =
"PageLoad.Experimental.NavigationToInteractive";
const char kHistogramInteractiveToInteractiveDetection[] =
"PageLoad.Internal.InteractiveToInteractiveDetection";
const char kHistogramFirstInputDelay[] =
"PageLoad.InteractiveTiming.FirstInputDelay";
const char kHistogramFirstInputTimestamp[] =
"PageLoad.InteractiveTiming.FirstInputTimestamp";
const char kHistogramParseStartToFirstMeaningfulPaint[] =
"PageLoad.Experimental.PaintTiming.ParseStartToFirstMeaningfulPaint";
const char kHistogramParseStartToFirstContentfulPaint[] =
"PageLoad.PaintTiming.ParseStartToFirstContentfulPaint";
const char kBackgroundHistogramParseStartToFirstContentfulPaint[] =
"PageLoad.PaintTiming.ParseStartToFirstContentfulPaint.Background";
const char kHistogramParseStart[] =
"PageLoad.ParseTiming.NavigationToParseStart";
const char kBackgroundHistogramParseStart[] =
"PageLoad.ParseTiming.NavigationToParseStart.Background";
const char kHistogramParseDuration[] = "PageLoad.ParseTiming.ParseDuration";
const char kBackgroundHistogramParseDuration[] =
"PageLoad.ParseTiming.ParseDuration.Background";
const char kHistogramParseBlockedOnScriptLoad[] =
"PageLoad.ParseTiming.ParseBlockedOnScriptLoad";
const char kBackgroundHistogramParseBlockedOnScriptLoad[] =
"PageLoad.ParseTiming.ParseBlockedOnScriptLoad.Background";
const char kHistogramParseBlockedOnScriptLoadDocumentWrite[] =
"PageLoad.ParseTiming.ParseBlockedOnScriptLoadFromDocumentWrite";
const char kBackgroundHistogramParseBlockedOnScriptLoadDocumentWrite[] =
"PageLoad.ParseTiming.ParseBlockedOnScriptLoadFromDocumentWrite."
"Background";
const char kHistogramParseBlockedOnScriptExecution[] =
"PageLoad.ParseTiming.ParseBlockedOnScriptExecution";
const char kHistogramParseBlockedOnScriptExecutionDocumentWrite[] =
"PageLoad.ParseTiming.ParseBlockedOnScriptExecutionFromDocumentWrite";
const char kHistogramFirstContentfulPaintNoStore[] =
"PageLoad.PaintTiming.NavigationToFirstContentfulPaint.NoStore";
const char kHistogramLoadTypeFirstContentfulPaintReload[] =
"PageLoad.PaintTiming.NavigationToFirstContentfulPaint.LoadType."
"Reload";
const char kHistogramLoadTypeFirstContentfulPaintReloadByGesture[] =
"PageLoad.PaintTiming.NavigationToFirstContentfulPaint.LoadType."
"Reload.UserGesture";
const char kHistogramLoadTypeFirstContentfulPaintForwardBack[] =
"PageLoad.PaintTiming.NavigationToFirstContentfulPaint.LoadType."
"ForwardBackNavigation";
const char kHistogramLoadTypeFirstContentfulPaintForwardBackNoStore[] =
"PageLoad.PaintTiming.NavigationToFirstContentfulPaint.LoadType."
"ForwardBackNavigation.NoStore";
const char kHistogramLoadTypeFirstContentfulPaintNewNavigation[] =
"PageLoad.PaintTiming.NavigationToFirstContentfulPaint.LoadType."
"NewNavigation";
const char kHistogramPageTimingForegroundDuration[] =
"PageLoad.PageTiming.ForegroundDuration";
const char kHistogramPageTimingForegroundDurationAfterPaint[] =
"PageLoad.PageTiming.ForegroundDuration.AfterPaint";
const char kHistogramPageTimingForegroundDurationNoCommit[] =
"PageLoad.PageTiming.ForegroundDuration.NoCommit";
const char kHistogramPageTimingForegroundDurationWithPaint[] =
"PageLoad.PageTiming.ForegroundDuration.WithPaint";
const char kHistogramPageTimingForegroundDurationWithoutPaint[] =
"PageLoad.PageTiming.ForegroundDuration.WithoutPaint";
const char kHistogramLoadTypeParseStartReload[] =
"PageLoad.ParseTiming.NavigationToParseStart.LoadType.Reload";
const char kHistogramLoadTypeParseStartForwardBack[] =
"PageLoad.ParseTiming.NavigationToParseStart.LoadType."
"ForwardBackNavigation";
const char kHistogramLoadTypeParseStartForwardBackNoStore[] =
"PageLoad.ParseTiming.NavigationToParseStart.LoadType."
"ForwardBackNavigation.NoStore";
const char kHistogramLoadTypeParseStartNewNavigation[] =
"PageLoad.ParseTiming.NavigationToParseStart.LoadType.NewNavigation";
const char kHistogramFirstForeground[] =
"PageLoad.PageTiming.NavigationToFirstForeground";
const char kHistogramFailedProvisionalLoad[] =
"PageLoad.PageTiming.NavigationToFailedProvisionalLoad";
const char kHistogramUserGestureNavigationToForwardBack[] =
"PageLoad.PageTiming.ForegroundDuration.PageEndReason."
"ForwardBackNavigation.UserGesture";
const char kHistogramForegroundToFirstPaint[] =
"PageLoad.PaintTiming.ForegroundToFirstPaint";
const char kHistogramForegroundToFirstContentfulPaint[] =
"PageLoad.PaintTiming.ForegroundToFirstContentfulPaint";
const char kHistogramForegroundToFirstMeaningfulPaint[] =
"PageLoad.Experimental.PaintTiming.ForegroundToFirstMeaningfulPaint";
const char kHistogramFirstContentfulPaintUserInitiated[] =
"PageLoad.PaintTiming.NavigationToFirstContentfulPaint.UserInitiated";
const char kHistogramFirstMeaningfulPaintStatus[] =
"PageLoad.Experimental.PaintTiming.FirstMeaningfulPaintStatus";
const char kHistogramTimeToInteractiveStatus[] =
"PageLoad.Experimental.TimeToInteractiveStatus";
const char kHistogramFirstNonScrollInputAfterFirstPaint[] =
"PageLoad.InputTiming.NavigationToFirstNonScroll.AfterPaint";
const char kHistogramFirstScrollInputAfterFirstPaint[] =
"PageLoad.InputTiming.NavigationToFirstScroll.AfterPaint";
const char kHistogramPageLoadTotalBytes[] = "PageLoad.Experimental.Bytes.Total";
const char kHistogramPageLoadNetworkBytes[] =
"PageLoad.Experimental.Bytes.Network";
const char kHistogramPageLoadCacheBytes[] = "PageLoad.Experimental.Bytes.Cache";
const char kHistogramLoadTypeTotalBytesForwardBack[] =
"PageLoad.Experimental.Bytes.Total.LoadType.ForwardBackNavigation";
const char kHistogramLoadTypeNetworkBytesForwardBack[] =
"PageLoad.Experimental.Bytes.Network.LoadType.ForwardBackNavigation";
const char kHistogramLoadTypeCacheBytesForwardBack[] =
"PageLoad.Experimental.Bytes.Cache.LoadType.ForwardBackNavigation";
const char kHistogramLoadTypeTotalBytesReload[] =
"PageLoad.Experimental.Bytes.Total.LoadType.Reload";
const char kHistogramLoadTypeNetworkBytesReload[] =
"PageLoad.Experimental.Bytes.Network.LoadType.Reload";
const char kHistogramLoadTypeCacheBytesReload[] =
"PageLoad.Experimental.Bytes.Cache.LoadType.Reload";
const char kHistogramLoadTypeTotalBytesNewNavigation[] =
"PageLoad.Experimental.Bytes.Total.LoadType.NewNavigation";
const char kHistogramLoadTypeNetworkBytesNewNavigation[] =
"PageLoad.Experimental.Bytes.Network.LoadType.NewNavigation";
const char kHistogramLoadTypeCacheBytesNewNavigation[] =
"PageLoad.Experimental.Bytes.Cache.LoadType.NewNavigation";
const char kHistogramTotalCompletedResources[] =
"PageLoad.Experimental.CompletedResources.Total";
const char kHistogramNetworkCompletedResources[] =
"PageLoad.Experimental.CompletedResources.Network";
const char kHistogramCacheCompletedResources[] =
"PageLoad.Experimental.CompletedResources.Cache";
} // namespace internal
CorePageLoadMetricsObserver::CorePageLoadMetricsObserver()
: transition_(ui::PAGE_TRANSITION_LINK),
was_no_store_main_resource_(false),
num_cache_resources_(0),
num_network_resources_(0),
cache_bytes_(0),
network_bytes_(0),
redirect_chain_size_(0) {}
CorePageLoadMetricsObserver::~CorePageLoadMetricsObserver() {}
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
CorePageLoadMetricsObserver::OnRedirect(
content::NavigationHandle* navigation_handle) {
redirect_chain_size_++;
return CONTINUE_OBSERVING;
}
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
CorePageLoadMetricsObserver::OnCommit(
content::NavigationHandle* navigation_handle,
ukm::SourceId source_id) {
transition_ = navigation_handle->GetPageTransition();
const net::HttpResponseHeaders* headers =
navigation_handle->GetResponseHeaders();
if (headers) {
was_no_store_main_resource_ =
headers->HasHeaderValue("cache-control", "no-store");
}
UMA_HISTOGRAM_COUNTS_100("PageLoad.Navigation.RedirectChainLength",
redirect_chain_size_);
return CONTINUE_OBSERVING;
}
void CorePageLoadMetricsObserver::OnDomContentLoadedEventStart(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
if (WasStartedInForegroundOptionalEventInForeground(
timing.document_timing->dom_content_loaded_event_start, info)) {
PAGE_LOAD_HISTOGRAM(
internal::kHistogramDomContentLoaded,
timing.document_timing->dom_content_loaded_event_start.value());
} else {
PAGE_LOAD_HISTOGRAM(
internal::kBackgroundHistogramDomContentLoaded,
timing.document_timing->dom_content_loaded_event_start.value());
}
}
void CorePageLoadMetricsObserver::OnLoadEventStart(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
if (WasStartedInForegroundOptionalEventInForeground(
timing.document_timing->load_event_start, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramLoad,
timing.document_timing->load_event_start.value());
} else {
PAGE_LOAD_HISTOGRAM(internal::kBackgroundHistogramLoad,
timing.document_timing->load_event_start.value());
}
}
void CorePageLoadMetricsObserver::OnFirstLayout(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
if (WasStartedInForegroundOptionalEventInForeground(
timing.document_timing->first_layout, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstLayout,
timing.document_timing->first_layout.value());
} else {
PAGE_LOAD_HISTOGRAM(internal::kBackgroundHistogramFirstLayout,
timing.document_timing->first_layout.value());
}
}
void CorePageLoadMetricsObserver::OnFirstPaintInPage(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
first_paint_ =
info.navigation_start + timing.paint_timing->first_paint.value();
if (WasStartedInForegroundOptionalEventInForeground(
timing.paint_timing->first_paint, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstPaint,
timing.paint_timing->first_paint.value());
} else {
PAGE_LOAD_HISTOGRAM(internal::kBackgroundHistogramFirstPaint,
timing.paint_timing->first_paint.value());
}
if (WasStartedInBackgroundOptionalEventInForeground(
timing.paint_timing->first_paint, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramForegroundToFirstPaint,
timing.paint_timing->first_paint.value() -
info.first_foreground_time.value());
}
}
void CorePageLoadMetricsObserver::OnFirstTextPaintInPage(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
if (WasStartedInForegroundOptionalEventInForeground(
timing.paint_timing->first_text_paint, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstTextPaint,
timing.paint_timing->first_text_paint.value());
} else {
PAGE_LOAD_HISTOGRAM(internal::kBackgroundHistogramFirstTextPaint,
timing.paint_timing->first_text_paint.value());
}
}
void CorePageLoadMetricsObserver::OnFirstImagePaintInPage(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
if (WasStartedInForegroundOptionalEventInForeground(
timing.paint_timing->first_image_paint, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstImagePaint,
timing.paint_timing->first_image_paint.value());
} else {
PAGE_LOAD_HISTOGRAM(internal::kBackgroundHistogramFirstImagePaint,
timing.paint_timing->first_image_paint.value());
}
}
void CorePageLoadMetricsObserver::OnFirstContentfulPaintInPage(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
if (WasStartedInForegroundOptionalEventInForeground(
timing.paint_timing->first_contentful_paint, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstContentfulPaint,
timing.paint_timing->first_contentful_paint.value());
PAGE_LOAD_HISTOGRAM(internal::kHistogramParseStartToFirstContentfulPaint,
timing.paint_timing->first_contentful_paint.value() -
timing.parse_timing->parse_start.value());
if (was_no_store_main_resource_) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstContentfulPaintNoStore,
timing.paint_timing->first_contentful_paint.value());
}
// TODO(bmcquade): consider adding a histogram that uses
// UserInputInfo.user_input_event.
if (info.user_initiated_info.browser_initiated ||
info.user_initiated_info.user_gesture) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstContentfulPaintUserInitiated,
timing.paint_timing->first_contentful_paint.value());
}
switch (GetPageLoadType(transition_)) {
case LOAD_TYPE_RELOAD:
PAGE_LOAD_HISTOGRAM(
internal::kHistogramLoadTypeFirstContentfulPaintReload,
timing.paint_timing->first_contentful_paint.value());
// TODO(bmcquade): consider adding a histogram that uses
// UserInputInfo.user_input_event.
if (info.user_initiated_info.browser_initiated ||
info.user_initiated_info.user_gesture) {
PAGE_LOAD_HISTOGRAM(
internal::kHistogramLoadTypeFirstContentfulPaintReloadByGesture,
timing.paint_timing->first_contentful_paint.value());
}
break;
case LOAD_TYPE_FORWARD_BACK:
PAGE_LOAD_HISTOGRAM(
internal::kHistogramLoadTypeFirstContentfulPaintForwardBack,
timing.paint_timing->first_contentful_paint.value());
if (was_no_store_main_resource_) {
PAGE_LOAD_HISTOGRAM(
internal::
kHistogramLoadTypeFirstContentfulPaintForwardBackNoStore,
timing.paint_timing->first_contentful_paint.value());
}
break;
case LOAD_TYPE_NEW_NAVIGATION:
PAGE_LOAD_HISTOGRAM(
internal::kHistogramLoadTypeFirstContentfulPaintNewNavigation,
timing.paint_timing->first_contentful_paint.value());
break;
case LOAD_TYPE_NONE:
NOTREACHED();
break;
}
} else {
PAGE_LOAD_HISTOGRAM(internal::kBackgroundHistogramFirstContentfulPaint,
timing.paint_timing->first_contentful_paint.value());
PAGE_LOAD_HISTOGRAM(
internal::kBackgroundHistogramParseStartToFirstContentfulPaint,
timing.paint_timing->first_contentful_paint.value() -
timing.parse_timing->parse_start.value());
}
if (WasStartedInBackgroundOptionalEventInForeground(
timing.paint_timing->first_contentful_paint, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramForegroundToFirstContentfulPaint,
timing.paint_timing->first_contentful_paint.value() -
info.first_foreground_time.value());
}
}
void CorePageLoadMetricsObserver::OnFirstMeaningfulPaintInMainFrameDocument(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
if (WasStartedInForegroundOptionalEventInForeground(
timing.paint_timing->first_meaningful_paint, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstMeaningfulPaint,
timing.paint_timing->first_meaningful_paint.value());
PAGE_LOAD_HISTOGRAM(internal::kHistogramParseStartToFirstMeaningfulPaint,
timing.paint_timing->first_meaningful_paint.value() -
timing.parse_timing->parse_start.value());
RecordFirstMeaningfulPaintStatus(internal::FIRST_MEANINGFUL_PAINT_RECORDED);
} else {
RecordFirstMeaningfulPaintStatus(
internal::FIRST_MEANINGFUL_PAINT_BACKGROUNDED);
}
if (WasStartedInBackgroundOptionalEventInForeground(
timing.paint_timing->first_meaningful_paint, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramForegroundToFirstMeaningfulPaint,
timing.paint_timing->first_meaningful_paint.value() -
info.first_foreground_time.value());
}
}
void CorePageLoadMetricsObserver::OnPageInteractive(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
// Both interactive and interactive detection time must be present.
DCHECK(timing.interactive_timing->interactive);
DCHECK(timing.interactive_timing->interactive_detection);
DCHECK_GE(timing.interactive_timing->interactive_detection.value(),
timing.interactive_timing->interactive.value());
if (!WasStartedInForegroundOptionalEventInForeground(
timing.interactive_timing->interactive_detection, info)) {
RecordTimeToInteractiveStatus(internal::TIME_TO_INTERACTIVE_BACKGROUNDED);
return;
}
if (timing.interactive_timing->first_invalidating_input &&
timing.interactive_timing->first_invalidating_input.value() <
timing.interactive_timing->interactive) {
RecordTimeToInteractiveStatus(
internal::TIME_TO_INTERACTIVE_USER_INTERACTION_BEFORE_INTERACTIVE);
return;
}
base::TimeDelta time_to_interactive =
timing.interactive_timing->interactive.value();
base::TimeDelta interactive_to_detection =
timing.interactive_timing->interactive_detection.value() -
time_to_interactive;
PAGE_LOAD_HISTOGRAM(internal::kHistogramTimeToInteractive,
time_to_interactive);
PAGE_LOAD_HISTOGRAM(internal::kHistogramInteractiveToInteractiveDetection,
interactive_to_detection);
RecordTimeToInteractiveStatus(internal::TIME_TO_INTERACTIVE_RECORDED);
}
void CorePageLoadMetricsObserver::OnFirstInputInPage(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& extra_info) {
if (!WasStartedInForegroundOptionalEventInForeground(
timing.interactive_timing->first_input_timestamp, extra_info)) {
return;
}
// Input delay will often be ~0, and will only be > 10 seconds very
// rarely. Capture the range from 1ms to 60s.
UMA_HISTOGRAM_CUSTOM_TIMES(
internal::kHistogramFirstInputDelay,
timing.interactive_timing->first_input_delay.value(),
base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(60),
50);
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstInputTimestamp,
timing.interactive_timing->first_input_timestamp.value());
}
void CorePageLoadMetricsObserver::OnParseStart(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
if (WasStartedInForegroundOptionalEventInForeground(
timing.parse_timing->parse_start, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramParseStart,
timing.parse_timing->parse_start.value());
switch (GetPageLoadType(transition_)) {
case LOAD_TYPE_RELOAD:
PAGE_LOAD_HISTOGRAM(internal::kHistogramLoadTypeParseStartReload,
timing.parse_timing->parse_start.value());
break;
case LOAD_TYPE_FORWARD_BACK:
PAGE_LOAD_HISTOGRAM(internal::kHistogramLoadTypeParseStartForwardBack,
timing.parse_timing->parse_start.value());
if (was_no_store_main_resource_) {
PAGE_LOAD_HISTOGRAM(
internal::kHistogramLoadTypeParseStartForwardBackNoStore,
timing.parse_timing->parse_start.value());
}
break;
case LOAD_TYPE_NEW_NAVIGATION:
PAGE_LOAD_HISTOGRAM(internal::kHistogramLoadTypeParseStartNewNavigation,
timing.parse_timing->parse_start.value());
break;
case LOAD_TYPE_NONE:
NOTREACHED();
break;
}
} else {
PAGE_LOAD_HISTOGRAM(internal::kBackgroundHistogramParseStart,
timing.parse_timing->parse_start.value());
}
}
void CorePageLoadMetricsObserver::OnParseStop(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
base::TimeDelta parse_duration = timing.parse_timing->parse_stop.value() -
timing.parse_timing->parse_start.value();
if (WasStartedInForegroundOptionalEventInForeground(
timing.parse_timing->parse_stop, info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramParseDuration, parse_duration);
PAGE_LOAD_HISTOGRAM(
internal::kHistogramParseBlockedOnScriptLoad,
timing.parse_timing->parse_blocked_on_script_load_duration.value());
PAGE_LOAD_HISTOGRAM(
internal::kHistogramParseBlockedOnScriptLoadDocumentWrite,
timing.parse_timing
->parse_blocked_on_script_load_from_document_write_duration
.value());
PAGE_LOAD_HISTOGRAM(
internal::kHistogramParseBlockedOnScriptExecution,
timing.parse_timing->parse_blocked_on_script_execution_duration
.value());
PAGE_LOAD_HISTOGRAM(
internal::kHistogramParseBlockedOnScriptExecutionDocumentWrite,
timing.parse_timing
->parse_blocked_on_script_execution_from_document_write_duration
.value());
} else {
PAGE_LOAD_HISTOGRAM(internal::kBackgroundHistogramParseDuration,
parse_duration);
PAGE_LOAD_HISTOGRAM(
internal::kBackgroundHistogramParseBlockedOnScriptLoad,
timing.parse_timing->parse_blocked_on_script_load_duration.value());
PAGE_LOAD_HISTOGRAM(
internal::kBackgroundHistogramParseBlockedOnScriptLoadDocumentWrite,
timing.parse_timing
->parse_blocked_on_script_load_from_document_write_duration
.value());
}
}
void CorePageLoadMetricsObserver::OnComplete(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
RecordTimingHistograms(timing, info);
RecordByteAndResourceHistograms(timing, info);
RecordForegroundDurationHistograms(timing, info, base::TimeTicks());
}
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
CorePageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
// FlushMetricsOnAppEnterBackground is invoked on Android in cases where the
// app is about to be backgrounded, as part of the Activity.onPause()
// flow. After this method is invoked, Chrome may be killed without further
// notification, so we record final metrics collected up to this point.
if (info.did_commit) {
RecordTimingHistograms(timing, info);
RecordByteAndResourceHistograms(timing, info);
}
RecordForegroundDurationHistograms(timing, info, base::TimeTicks::Now());
return STOP_OBSERVING;
}
void CorePageLoadMetricsObserver::OnFailedProvisionalLoad(
const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info,
const page_load_metrics::PageLoadExtraInfo& extra_info) {
// Only handle actual failures; provisional loads that failed due to another
// committed load or due to user action are recorded in
// AbortsPageLoadMetricsObserver.
if (failed_load_info.error != net::OK &&
failed_load_info.error != net::ERR_ABORTED) {
if (WasStartedInForegroundOptionalEventInForeground(
failed_load_info.time_to_failed_provisional_load, extra_info)) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramFailedProvisionalLoad,
failed_load_info.time_to_failed_provisional_load);
}
}
// Provide an empty PageLoadTiming, since we don't have any timing metrics
// for failed provisional loads.
RecordForegroundDurationHistograms(page_load_metrics::mojom::PageLoadTiming(),
extra_info, base::TimeTicks());
}
void CorePageLoadMetricsObserver::OnUserInput(
const blink::WebInputEvent& event) {
base::TimeTicks now;
if (first_paint_.is_null())
return;
if (!received_non_scroll_input_after_first_paint_) {
if (event.GetType() == blink::WebInputEvent::kGestureTap ||
event.GetType() == blink::WebInputEvent::kMouseUp) {
received_non_scroll_input_after_first_paint_ = true;
if (now.is_null())
now = base::TimeTicks::Now();
PAGE_LOAD_HISTOGRAM(
internal::kHistogramFirstNonScrollInputAfterFirstPaint,
now - first_paint_);
}
}
if (!received_scroll_input_after_first_paint_ &&
event.GetType() == blink::WebInputEvent::kGestureScrollBegin) {
received_scroll_input_after_first_paint_ = true;
if (now.is_null())
now = base::TimeTicks::Now();
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstScrollInputAfterFirstPaint,
now - first_paint_);
}
}
void CorePageLoadMetricsObserver::OnLoadedResource(
const page_load_metrics::ExtraRequestCompleteInfo&
extra_request_complete_info) {
if (extra_request_complete_info.was_cached) {
++num_cache_resources_;
cache_bytes_ += extra_request_complete_info.raw_body_bytes;
} else {
++num_network_resources_;
network_bytes_ += extra_request_complete_info.raw_body_bytes;
}
}
// This method records values for metrics that were not recorded during any
// other event, or records failure status for metrics that have not been
// collected yet. This is meant to be called at the end of a page lifetime, for
// example, when the user is navigating away from the page.
void CorePageLoadMetricsObserver::RecordTimingHistograms(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
// Log time to first foreground / time to first background. Log counts that we
// started a relevant page load in the foreground / background.
if (!info.started_in_foreground && info.first_foreground_time) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstForeground,
info.first_foreground_time.value());
}
if (timing.paint_timing->first_paint &&
!timing.paint_timing->first_meaningful_paint) {
RecordFirstMeaningfulPaintStatus(
timing.paint_timing->first_contentful_paint
? internal::FIRST_MEANINGFUL_PAINT_DID_NOT_REACH_NETWORK_STABLE
: internal::
FIRST_MEANINGFUL_PAINT_DID_NOT_REACH_FIRST_CONTENTFUL_PAINT);
}
if (timing.paint_timing->first_paint &&
!timing.interactive_timing->interactive) {
RecordTimeToInteractiveStatus(
timing.paint_timing->first_meaningful_paint
? internal::TIME_TO_INTERACTIVE_DID_NOT_REACH_QUIESCENCE
: internal::
TIME_TO_INTERACTIVE_DID_NOT_REACH_FIRST_MEANINGFUL_PAINT);
}
}
void CorePageLoadMetricsObserver::RecordForegroundDurationHistograms(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info,
base::TimeTicks app_background_time) {
base::Optional<base::TimeDelta> foreground_duration =
GetInitialForegroundDuration(info, app_background_time);
if (!foreground_duration)
return;
if (info.did_commit) {
PAGE_LOAD_LONG_HISTOGRAM(internal::kHistogramPageTimingForegroundDuration,
foreground_duration.value());
if (timing.paint_timing->first_paint &&
timing.paint_timing->first_paint < foreground_duration) {
PAGE_LOAD_LONG_HISTOGRAM(
internal::kHistogramPageTimingForegroundDurationAfterPaint,
foreground_duration.value() -
timing.paint_timing->first_paint.value());
PAGE_LOAD_LONG_HISTOGRAM(
internal::kHistogramPageTimingForegroundDurationWithPaint,
foreground_duration.value());
} else {
PAGE_LOAD_LONG_HISTOGRAM(
internal::kHistogramPageTimingForegroundDurationWithoutPaint,
foreground_duration.value());
}
} else {
PAGE_LOAD_LONG_HISTOGRAM(
internal::kHistogramPageTimingForegroundDurationNoCommit,
foreground_duration.value());
}
if (info.page_end_reason == page_load_metrics::END_FORWARD_BACK &&
info.user_initiated_info.user_gesture &&
!info.user_initiated_info.browser_initiated &&
info.page_end_time <= foreground_duration) {
PAGE_LOAD_HISTOGRAM(internal::kHistogramUserGestureNavigationToForwardBack,
info.page_end_time.value());
}
}
void CorePageLoadMetricsObserver::RecordByteAndResourceHistograms(
const page_load_metrics::mojom::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& info) {
DCHECK_GE(network_bytes_, 0);
DCHECK_GE(cache_bytes_, 0);
int64_t total_bytes = network_bytes_ + cache_bytes_;
PAGE_BYTES_HISTOGRAM(internal::kHistogramPageLoadNetworkBytes,
network_bytes_);
PAGE_BYTES_HISTOGRAM(internal::kHistogramPageLoadCacheBytes, cache_bytes_);
PAGE_BYTES_HISTOGRAM(internal::kHistogramPageLoadTotalBytes, total_bytes);
switch (GetPageLoadType(transition_)) {
case LOAD_TYPE_RELOAD:
PAGE_BYTES_HISTOGRAM(internal::kHistogramLoadTypeNetworkBytesReload,
network_bytes_);
PAGE_BYTES_HISTOGRAM(internal::kHistogramLoadTypeCacheBytesReload,
cache_bytes_);
PAGE_BYTES_HISTOGRAM(internal::kHistogramLoadTypeTotalBytesReload,
total_bytes);
break;
case LOAD_TYPE_FORWARD_BACK:
PAGE_BYTES_HISTOGRAM(internal::kHistogramLoadTypeNetworkBytesForwardBack,
network_bytes_);
PAGE_BYTES_HISTOGRAM(internal::kHistogramLoadTypeCacheBytesForwardBack,
cache_bytes_);
PAGE_BYTES_HISTOGRAM(internal::kHistogramLoadTypeTotalBytesForwardBack,
total_bytes);
break;
case LOAD_TYPE_NEW_NAVIGATION:
PAGE_BYTES_HISTOGRAM(
internal::kHistogramLoadTypeNetworkBytesNewNavigation,
network_bytes_);
PAGE_BYTES_HISTOGRAM(internal::kHistogramLoadTypeCacheBytesNewNavigation,
cache_bytes_);
PAGE_BYTES_HISTOGRAM(internal::kHistogramLoadTypeTotalBytesNewNavigation,
total_bytes);
break;
case LOAD_TYPE_NONE:
NOTREACHED();
break;
}
PAGE_RESOURCE_COUNT_HISTOGRAM(internal::kHistogramNetworkCompletedResources,
num_network_resources_);
PAGE_RESOURCE_COUNT_HISTOGRAM(internal::kHistogramCacheCompletedResources,
num_cache_resources_);
PAGE_RESOURCE_COUNT_HISTOGRAM(internal::kHistogramTotalCompletedResources,
num_cache_resources_ + num_network_resources_);
}