Page load metrics can be tracked in chromium by implementing the PageLoadMetricsObserver interface, which provides callbacks that indicate when various paint, document, and user interaction events happen during the course of a page load.
This document provides a detailed guide to implementing a PageLoadMetricsObserver. For a quick introduction, you may wish to read the implementation basics section below, browse the PageLoadMetricsObserverInterface, and look over existing PageLoadMetricsObserver implementations. You can refer to AssertPageLoadMetricsObserver for context and assumptions that callbacks can be rely on.
PageLoadMetricsObservers can track web vitals like time to Largest Contentful Paint and other per-page metrics such as time to first user input in UMA or UKM.
You can implement a PageLoadMetricsObserver to:
DataSaverSiteBreakdownMetricsObserverHttpsEngagementPageLoadMetricsObserverAndroidPageLoadMetricsObserverIf you have additional questions or needs not covered in this document, please contact speed-metrics-dev@chromium.org.
The rest of this document assumes familiarity with the page load lifecycle in Chrome. Key concepts are explained briefly here:
For more detailed information, see the comments in the navigation section of WebContentsObserver, as well as the navigation-related entries in Truths and Not Truths.
Page load metrics are tracked by implementing the PageLoadMetricsObserver interface. A new instance of each PageLoadMetricsObserver implementation is instantiated for each page load. PageLoadMetricsObserver instances are created and owned by the page load metrics infrastructure. A PageLoadMetricsObserver’s lifetime starts when a main frame navigation is initiated, and ends when one of the following cases is encountered:
WebContents (a WebContents is essentially a browser tab) is destroyedPageLoadMetricsObserver in question responds to the resulting OnEnterBackForwardCache callback with STOP_OBSERVING.PageLoadMetricsObserver lifetimes, for a WebContents with 3 page loadsAbove, we show example PageLoadMetricsObserver (PLMO) lifetimes for a WebContents with three main-frame navigations, shown in blue, green, and yellow, with time starting on the left, and ending on the right.
WebContents, in blue, is instantiated when its navigation starts, and is destroyed when the next navigation in the WebContents commits (the yellow load).WebContents is closed. The commit of the yellow navigation causes the PLMO for the previously committed load, in blue, to be destroyed.PageLoadMetricsObserver lifetimes within a WebContents often overlap. In particular, if there is a committed load in a WebContents, the lifetime of its associated PageLoadMetricsObserver overlaps with the lifetime of subsequent navigations, for the period from the subsequent load’s navigation start until the next navigation commits. We see this for the blue and yellow loads in the example above.
PageLoadMetricsObserver callback life cyclePageLoadMetricsObserver (PLMO) implementation is instantiated when a navigation starts. At this time, the OnStart callback is invoked.OnRedirect may be invoked one or more times, for each server-side redirect. Note that content-triggered redirects, such as meta refreshes or a JS-based navigations, are separate distinct page loads, and do not cause OnRedirect to be invoked.OnCommit is invokedOnFailedProvisionalLoad is invoked, and the PLMO is destroyed.OnComplete callback is invoked just before the PLMO is destroyed (due to e.g. a new page load committing, the associated WebContents being destroyed, etc).Returning to the example above, callbacks are invoked in the following order:
PageLoadMetricsObservers for the blue load are instantiated, and OnStart is invoked on them.OnCommit is invoked for the blue load’s PLMOs.OnStart is invoked for them.OnFailedProvisionalLoad is invoked for the green load’s PLMOs. PLMOs for the green navigation are then destroyed.OnStart is invoked on them.OnComplete is invoked for the blue load’s PLMOs. OnCommit is then invoked for the yellow load’s PLMOs.OnComplete is invoked for the yellow load’s PLMOs.Additionally, the following callbacks may be invoked during the lifetime of a PageLoadMetricsObserver:
OnHidden and OnShown are invoked as the associated WebContents is hidden or shown (e.g. when the user switches tabs).FlushMetricsOnAppEnterBackground callback is invoked to signal to observers that any metrics buffered in memory should be persisted. Once FlushMetricsOnAppEnterBackground is invoked, the application may be killed without subsequent notification. Note that FlushMetricsOnAppEnterBackground may be invoked multiple times for a single page load if Chrome transitions from the foreground to background to foreground to background without being killed. Note that this callback is only invoked if the entire Chrome application is backgrounded. It is not invoked if the user e.g. switches between tabs within Chrome.OnLoadedResource is invoked as each resource on the page finishes loading.OnFirstContentfulPaint are invoked as the associated page load timing events occur.OnUserInput is invoked as user input events are received.OnLoadingBehaviorObserved is invoked as certain page loading behaviors are observed. See the section on loading behaviors below for additional information.OnEnterBackForwardCache is invoked if the page will enter the Back/Forward cache. Observers may return STOP_OBSERVING from this callback if they do not wish to continue logging after a page has entered the BFCache, and this is the default implementation for PageLoadMetricsObserver (it will also call OnComplete at this time).OnRestoreFromBackForwardCache is invoked when the page is restored from the Back/Forward cache. This callback is invoked with a NavigationHandle which will have a different navigation ID to the original page load‘s. Whether metrics should be logged using this new navigation ID, or the original page load’s ID, depends on the desired interpretation of the metrics.PageLoadMetricsObservers?Only provisional loads that meet the following criteria are tracked:
PageLoadMetricsObserver::ShouldObserveScheme().In addition, when a load commits, additional criteria are applied at commit time. Only page loads that meet the earlier provisional criteria, as well as the following criteria at commit time, are tracked after commit:
Note that the provisional criteria is applied again at commit time, as navigation attributes such as the URL may have changed between the time a provisional load is started and the time it commits.
Loads that finish and received HTTP response headers but did not commit do not invoke the OnFailedProvisionalLoad callback. These are special case navigations, such as downloads and HTTP 204/205 responses, which complete successfully but do not result in a committed load.
If a page load is filtered due to the above criteria, no subsequent callbacks will be invoked for the associated PageLoadMetricsObservers for that page load.
Because not every load meets the commit-time criteria, some PageLoadMetricsObservers will see calls to OnStart and OnRedirect but no other callbacks.
We intend to allow teams to add custom PageLoad UMA metrics that relax these constraints as needed. For instance, we could add support for tracking just NTP loads, or chrome:// URLs. If you are interested in tracking metrics using a different set of constraints, please reach out to speed-metrics-dev@chromium.org.
PageLoadMetricsEmbedder::RegisterObservers method in page_load_metrics_initialize.cc.See https://chromium-review.googlesource.com/c/chromium/src/+/2294392 for an example end-to-end change that adds a new PageLoadMetricsObserver.
By default, page loads that spend time in the background during the period between navigation start and an event occurring are not included in core page load metrics. Backgrounded pages are often throttled and thus their metrics can be less useful/actionable for understanding page load performance.
For instance, the PageLoad.PaintTiming.NavigationToFirstContentfulPaint UMA only includes samples for page loads that were in the foreground for the entire period from navigation to first contentful paint. Page loads that were in the background for any period of time between navigation start and first contentful paint are excluded.
Thus, it is recommended that observers not record samples for metrics that occur when a page load is in the background.
An observer that does not want to track any metrics for events that occur after the page has been backgrounded can implement OnHidden to return STOP_OBSERVING, and OnStart to return STOP_OBSERVING if the started_in_foreground parameter is false.
Observers that track some background metrics can determine if an event happened completely in the foreground using the WasStartedInForegroundOptionalEventInForeground method. See existing page load metrics observer implementations for examples.
Observers that wish to track metrics for pages that spend time in the background can log metrics for those cases by checking that WasStartedInForegroundOptionalEventInForeground returns false, and using a separate histogram with a .Background suffix. For example, PageLoad.PaintTiming.NavigationToFirstContentfulPaint.Background includes page loads that were in the background at some point in time between navigation start and first contentful paint. In practice, we have not found .Background histograms to be very useful, other than to track the percentage of page loads that spend time in the background. Observers are discouraged from logging background histograms unless there is a clear reason to do so.
Most observers add their histograms under the PageLoad.Clients.* prefix. For example, PageLoad.Clients.DataReductionProxy.PaintTiming.NavigationToFirstContentfulPaint tracks PageLoad.PaintTiming.NavigationToFirstContentfulPaint for the subset of page loads loaded through the data reduction proxy. It is recommended that observers add metrics under PageLoad.Clients, as this allows other UMA users to more easily discover these metrics while browsing metrics with the PageLoad.* prefix.
If your metrics are not likely to be useful to other UMA users interested in page load metrics, you may wish to use a different naming scheme for your metrics. If you have questions, reach out to speed-metrics-dev@chromium.org.
On the Android platform, once the Chrome application goes into the background, it may be killed without subsequent notification. To avoid data loss, observers that buffer metric information in memory may wish to implement the FlushMetricsOnAppEnterBackground callback to persist metrics when Chrome goes into the background.
Considerations
OnComplete callback should consider also implementing the FlushMetricsOnAppEnterBackground callback.FlushMetricsOnAppEnterBackground may also receive an OnComplete callback.FlushMetricsOnAppEnterBackground may be invoked multiple times for a single page load if Chrome transitions from the foreground to background to foreground to background without being killed.PageLoadMetricsObserver examplesObservers that wish to avoid tracking metrics for some page loads can do so by returning ObservePolicy::STOP_OBSERVING from observer callbacks that return an ObservePolicy. For example, an observer that wishes to only track metrics for page loads that match certain criteria at commit, such as the hostname of the committed URL, can implement the OnCommit callback. If an observer returns STOP_OBSERVING, no subsequent callbacks will be invoked on the observer.
SignedExchangePageLoadMetricsObserver is an observer that performs page load filtering in the OnCommit callback, tracking only page loads for signed exchanges.
Considerations
PageLoadMetricsObservers that want to track pages with certain attributes known in Blink, for example, whether the page is service worker controlled or whether the page loaded a parser blocking script inserted via document.write, can do so using loading behaviors in the WebLoadingBehaviorFlag Blink public enum.
To track pages with a certain loading behavior, a PageLoadMetricsObserver can implement the OnLoadingBehaviorObserved callback, or check the metadata.behavior_flags field of the PageLoadExtraInfo structure that is passed to various PageLoadMetricsObserver callbacks.
ServiceWorkerPageLoadMetricsObserver is an observer that tracks metrics for page loads that are controlled by a service worker, as indicated by the WebLoadingBehaviorServiceWorkerControlled loading behavior flag.
Considerations
WebLoadingBehaviorDocumentWriteBlock loading behavior may be observed after first contentful paint occurs.OnLoadingBehaviorObserved callback.OnLoadingBehaviorObserved. DocumentWritePageLoadMetricsObserver is an observer that uses this logging strategy.If your observer wants to track a new loading behavior, you can add it to the WebLoadingBehaviorFlag enum and add instrumentation in Blink to notify the page load metrics infrastructure when the new loading behavior is observed. See the changes to FrameLoader.cpp in this change for an example change that adds instrumentation to track a new loading behavior.
Observers can track aborted page loads. A page load abort occurs when some event occurs that causes the page load to be terminated. Note that page load abort tracking is somewhat experimental and is subject to change.
Aborts can occur before or after commit. Observers can track aborts that happen before commit by implementing the OnFailedProvisionalLoad callback, and aborts that happen after commit by implementing the OnComplete callback. The current convention is to log aborts that occur before commit using the .BeforeCommit histogram suffix, and after commit using a suffix such as .AfterCommit.BeforePaint.
To determine whether an abort occurred, observers can use the PageLoadMetricsObserverDelegate API with the page_load_metrics::GetPageAbortInfo utility function. Observers should check whether the reason field of the PageAbortInfo structure has a value other than ABORT_NONE. The time until the page load was aborted is available in the time_to_abort field of the PageAbortInfo structure.
Some observer implementers may wish to track statistics aggregated across multiple page loads. Since a PageLoadMetricsObserver’s lifetime is bound to a single page load, a separate object with a longer lifetime must be used to track these statistics.
HttpsEngagementPageLoadMetricsObserver is an observer that tracks statistics aggregated across page loads. The observer forwards information about a page load to the HttpsEngagementService, which is responsible for aggregating statistics and logging aggregated metrics.