chromium/components/page_load_metrics/common/page_load_metrics.mojom

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

module page_load_metrics.mojom;

import "ui/gfx/geometry/mojom/geometry.mojom";
import "mojo/public/mojom/base/shared_memory.mojom";
import "mojo/public/mojom/base/time.mojom";
import
  "third_party/blink/public/mojom/loader/javascript_framework_detection.mojom";
import "services/network/public/mojom/request_priority.mojom";
import "third_party/blink/public/mojom/use_counter/use_counter_feature.mojom";

// TimeDeltas below relative to navigation start.
struct DocumentTiming {
  // Time immediately before the DOMContentLoaded event is fired.
  mojo_base.mojom.TimeDelta? dom_content_loaded_event_start;

  // Time immediately before the load event is fired.
  mojo_base.mojom.TimeDelta? load_event_start;
};

struct LcpResourceLoadTimings {
  // Time when the underlying LCP resource is requested.
  mojo_base.mojom.TimeDelta? discovery_time;

  // Time when the underlying LCP resource starts loading.
  mojo_base.mojom.TimeDelta? load_start;

  // Time when the underlying LCP resource finishes loading.
  mojo_base.mojom.TimeDelta? load_end;
};

struct LargestContentfulPaintTiming {
  // Time when the page's largest image is painted.
  mojo_base.mojom.TimeDelta? largest_image_paint;

  // Size of the largest image of the largest image paint, by
  // Size = Height * Width. Removed images are excluded.
  uint64 largest_image_paint_size;

  // Time when the page's largest text is painted.
  mojo_base.mojom.TimeDelta? largest_text_paint;

  // Size of the largest text of the largest text paint, by
  // Size = Height * Width. Removed text is excluded.
  uint64 largest_text_paint_size;

  LcpResourceLoadTimings resource_load_timings;

  // These are packed blink::LargestContentfulPaintType enums, indicating
  // the largest LCP candidate's type characteristics.
  uint64 type;

  // Computed entropy of the page's largest image, calculated as the image file
  // size, in bits, divided by the image's rendered size, in pixels.
  double image_bpp;

  // Loading priority used for the LCP image.
  // When the priority is not available or the LCP is a video,
  // `image_request_priority_valid` is false.
  // Otherwise, `image_request_priority_value` contains the priority.
  // TODO(crbug.com/40489779): Use an optional enum.
  bool image_request_priority_valid = false;
  network.mojom.RequestPriority image_request_priority_value;
};

// TimeDeltas below relative to navigation start.
struct PaintTiming {
  // Time when the first paint is performed.
  mojo_base.mojom.TimeDelta? first_paint;

  // Time when the first image is painted.
  mojo_base.mojom.TimeDelta? first_image_paint;

  // Time when the first contentful thing (image, text, etc.) is painted.
  mojo_base.mojom.TimeDelta? first_contentful_paint;

  // (Experimental) Time when the page's primary content is painted.
  mojo_base.mojom.TimeDelta? first_meaningful_paint;

  // Largest contentful paint, which includes removed content.
  LargestContentfulPaintTiming largest_contentful_paint;

  // (Experimental) largest contentful paint excluding removed content.
  LargestContentfulPaintTiming experimental_largest_contentful_paint;

  // (Experimental) Time when the frame is first eligible to be painted, i.e.
  // is first not render-throttled. Will be null if frame is throttled,
  // unless there has already been a |first_paint|.
  mojo_base.mojom.TimeDelta? first_eligible_to_paint;

  // (Experimental) Time when first input or scroll is received, causing the
  // largest contentful paint algorithm to stop.
  mojo_base.mojom.TimeDelta? first_input_or_scroll_notified_timestamp;
};

// TimeDeltas below represent durations of time during the page load.
struct ParseTiming {
  // Time that the document's parser started and stopped parsing main resource
  // content.
  mojo_base.mojom.TimeDelta? parse_start;
  mojo_base.mojom.TimeDelta? parse_stop;

  // Sum of times when the parser is blocked waiting on the load of a script.
  // This duration takes place between parser_start and parser_stop, and thus
  // must be less than or equal to parser_stop - parser_start. Note that this
  // value may be updated multiple times during the period between parse_start
  // and parse_stop.
  mojo_base.mojom.TimeDelta? parse_blocked_on_script_load_duration;

  // Sum of times when the parser is blocked waiting on the load of a script
  // that was inserted from document.write. This duration must be less than or
  // equal to parse_blocked_on_script_load_duration. Note that this value may be
  // updated multiple times during the period between parse_start and
  // parse_stop. Note that some uncommon cases where scripts are loaded via
  // document.write are not currently covered by this field. See crbug/600711
  // for details.
  mojo_base.mojom.TimeDelta? parse_blocked_on_script_load_from_document_write_duration;

  // Sum of times when the parser is executing a script.  This duration takes
  // place between parser_start and parser_stop, and thus must be less than or
  // equal to parser_stop - parser_start. Note that this value may be updated
  // multiple times during the period between parse_start and parse_stop.
  mojo_base.mojom.TimeDelta? parse_blocked_on_script_execution_duration;

  // Sum of times when the parser is executing a script that was inserted from
  // document.write. This duration must be less than or equal to
  // parse_blocked_on_script_load_duration. Note that this value may be updated
  // multiple times during the period between parse_start and parse_stop. Note
  // that some uncommon cases where scripts are loaded via document.write are
  // not currently covered by this field. See crbug/600711 for details.
  mojo_base.mojom.TimeDelta? parse_blocked_on_script_execution_from_document_write_duration;
};

struct InteractiveTiming {
  // Queueing Time of the first click, tap, key press, cancellable touchstart,
  // or pointer down followed by a pointer up.
  mojo_base.mojom.TimeDelta? first_input_delay;

  // The timestamp of the event whose delay is reported by GetFirstInputDelay().
  mojo_base.mojom.TimeDelta? first_input_timestamp;

  // The latency between user input and display update for the first scroll after
  // a navigation.
  mojo_base.mojom.TimeDelta? first_scroll_delay;

  // The timestamp of the user's first scroll after a navigation.
  mojo_base.mojom.TimeDelta? first_scroll_timestamp;
};

struct CustomUserTimingMark {
  // The mark name from performance.mark().
  string mark_name;
  // The startTime from performance.mark().
  mojo_base.mojom.TimeDelta start_time;
};

struct DomainLookupTiming {
  // Time that the domain lookup started relative to navigation start time.
  mojo_base.mojom.TimeDelta? domain_lookup_start;

  // Time that the domain lookup ended relative to navigation start time.
  mojo_base.mojom.TimeDelta? domain_lookup_end;
};

// PageLoadTiming contains timing metrics associated with a page load. Many of
// the metrics here are based on the Navigation Timing spec:
// http://www.w3.org/TR/navigation-timing/.
struct PageLoadTiming {
  // Time that the navigation for the associated page was initiated. Note that
  // this field is only used for internal tracking purposes and should not be
  // used by PageLoadMetricsObservers. This field will likely be removed in the
  // future.
  mojo_base.mojom.Time navigation_start;

  // Time relative to navigation_start that the network connection is going to
  // be established.
  mojo_base.mojom.TimeDelta? connect_start;

  // Time relative to navigation_start that the first byte of the response is
  // received.
  mojo_base.mojom.TimeDelta? response_start;
  DocumentTiming document_timing;
  InteractiveTiming interactive_timing;
  PaintTiming paint_timing;
  ParseTiming parse_timing;
  DomainLookupTiming domain_lookup_timing;

  // List of back-forward cache timings, one for each time a page was restored
  // from the cache.
  array<BackForwardCacheTiming> back_forward_cache_timings;

  // Time relative to navigation_start that the prerender activation navigation
  // was initiated. This is set for prerendered page loads that were later
  // activated.
  mojo_base.mojom.TimeDelta? activation_start;

  // Time between user input and navigation start. This is set for navigations
  // where the input start timing is known; currently when the navigation is
  // initiated by a link click in the renderer, or from the desktop omnibox.
  mojo_base.mojom.TimeDelta? input_to_navigation_start;

  // Time when the standard UserTiming mark `mark_fully_loaded` occurs.
  mojo_base.mojom.TimeDelta? user_timing_mark_fully_loaded;

  // Time when the standard UserTiming mark `mark_fully_visible` occurs.
  mojo_base.mojom.TimeDelta? user_timing_mark_fully_visible;

  // Time when the standard UserTiming mark `mark_interactive` occurs.
  mojo_base.mojom.TimeDelta? user_timing_mark_interactive;

  // If you add additional members, also be sure to update page_load_timing.h.
};

struct FrameMetadata {
  // These are packed blink::LoadingBehaviorFlag enums.
  int32 behavior_flags = 0;

  // For the main frame, the rect is the main frame document size (at (0,0));
  // for a subframe, the rect is frame's intersection rect with the main frame
  // in the main frame's coordinate system, and is an empty rect when there is
  // no intersection with the main frame. This is only set for the first time
  // the intersection rectangle is initially computed, and for any subsequent
  // changes, and is null otherwise (i.e. hasn't changed).
  gfx.mojom.Rect? main_frame_intersection_rect;

  // The main frame's viewport rectangle (encapsulating the dimensions and the
  // scroll position) in the main frame's coordinate system. This is only set
  // for the main frame, for the first time the viewport rectangle is initially
  // computed, and for any subsequent changes, and is null otherwise (i.e.
  // hasn't changed).
  gfx.mojom.Rect? main_frame_viewport_rect;

  // The image ad rectangles within the main frame. Empty rectangles are used to
  // signal the removal of the rectangle. The map keys are the element ids. This
  // is only populated for the main frame, for the first time
  // each rectangle is initially computed and for any subsequent changes.
  map<int32, gfx.mojom.Rect> main_frame_image_ad_rects;

  // Detected versions of JavaScript frameworks in the page.
  blink.mojom.JavaScriptFrameworkDetectionResult framework_detection_result;
};

struct SubresourceLoadMetrics {
  // Number of subresources loads for the frame.
  // Note that this include subresources where a service worker responded.
  uint32 number_of_subresources_loaded;

  // Number of subresources loads that is responded by the service worker.
  // i.e. `respondWith` was called.
  uint32 number_of_subresource_loads_handled_by_service_worker;

  // Metrics for service workers.
  ServiceWorkerSubresourceLoadMetrics? service_worker_subresource_load_metrics;
};

struct ServiceWorkerSubresourceLoadMetrics {
  // True even if one image sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool image_handled;
  // True even if one image sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool image_fallback;

  // True even if one CSS sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool css_handled;
  // True even if one CSS sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool css_fallback;

  // True even if one script sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool script_handled;
  // True even if one script sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool script_fallback;

  // True even if one font sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool font_handled;
  // True even if one font sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool font_fallback;

  // True even if one raw sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool raw_handled;
  // True even if one raw sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool raw_fallback;

  // True even if one SVG sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool svg_handled;
  // True even if one SVG sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool svg_fallback;

  // True even if one XSL sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool xsl_handled;
  // True even if one XSL sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool xsl_fallback;

  // True even if one Link Prefetch sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool link_prefetch_handled;
  // True even if one Link Prefetch sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool link_prefetch_fallback;

  // True even if one Text Track sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool text_track_handled;
  // True even if one Text Track sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool text_track_fallback;

  // True even if one audio sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool audio_handled;
  // True even if one audio sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool audio_fallback;

  // True even if one video sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool video_handled;
  // True even if one video sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool video_fallback;

  // True even if one manifest sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool manifest_handled;
  // True even if one manifest sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool manifest_fallback;

  // True even if one speculation rules sub resource is handled by a service
  // worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool speculation_rules_handled;
  // True even if one speculation rules sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool speculation_rules_fallback;

  // True even if one mock sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool mock_handled;
  // True even if one mock sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool mock_fallback;

  // True even if one dictionary sub resource is handled by a service worker.
  // i.e. the service worker called `respondWith` to return the resource.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was handled.
  bool dictionary_handled;
  // True even if one dictionary sub resource caused a network fallback.
  // i.e. the service worker did not call `respondWith`, and network fallback.
  // e.g. if there are two resources, and one is handed by the service worker,
  // and one did not, this flag will be true because one was network fallback.
  bool dictionary_fallback;

  // Total number of sub-resources which were matched to
  // `RouterSourceEnum.cache` in ServiceWorker Static Routing API, from
  // navigation start until onload event.
  uint32 matched_cache_router_source_count = 0;

  // Total number of sub-resources which were matched to
  // `RouterSourceEnum.fetch-event` in ServiceWorker Static Routing API, from
  // navigation start until onload event.
  uint32 matched_fetch_event_router_source_count = 0;

  // Total number of sub-resources which were matched to
  // `RouterSourceEnum.network` in ServiceWorker Static Routing API, from
  // navigation start until onload event.
  uint32 matched_network_router_source_count = 0;

  // Total number of sub-resources which were matched to the
  // `RouterSourceEnum.race-network-and-fetch-handler` in ServiceWorker Static
  // Routing API, from navigation start till onload event.
  uint32 matched_race_network_and_fetch_router_source_count = 0;

  // Total router evaluation time of ServiceWorker Static Routing API for
  // sub-resources.
  mojo_base.mojom.TimeDelta total_router_evaluation_time_for_subresources;
};

// Enumeration of distinct cache types.
enum CacheType {
  kNotCached, // Resource came from network.
  kHttp, // Resource was serviced by the http cache.
  kMemory, // Resource was serviced by the Renderer's MemoryCache.
};

struct ResourceDataUpdate {
  // The id for the resource request.
  int32 request_id = 0;

  // Network bytes received for the resource since the last timing update
  // from renderer to browser.
  int64 delta_bytes = 0;

  // Total network bytes received for the resource across timing updates. This
  // is the aggregate of the |delta_bytes| from each timing update.
  int64 received_data_length = 0;

  // The length of the response body for the resource before removing any
  // content encodings. Only set for complete resources.
  int64 encoded_body_length = 0;

  // The length of the response body in bytes for the resource after decoding.
  // Only set for complete resources.
  int64 decoded_body_length = 0;

  // Whether this resource load has completed.
  bool is_complete;

  // Whether this resource was tagged as an ad in the renderer. This flag can
  // be set to true at any point during a resource load. A more recent
  // ResourceDataUpdate can have a different flag than the previous update.
  // Once this is set to true, it will be true for all future updates.
  bool reported_as_ad_resource;

  // Whether this resource was loaded in the top-level frame.
  bool is_main_frame_resource;

  // Which cache this resource originated from, if any.
  CacheType cache_type;

  // Whether this resource is the primary resource for a frame.
  bool is_primary_frame_resource;

  // Mime type for the resource found in the network response header.
  string mime_type;

  // Whether the scheme of this resource indicates a secure connection.
  bool is_secure_scheme;

  // Whether this resource was fetched via proxy.
  bool proxy_used;
};

// Timestamp and layout shift score of a layout shift.
struct LayoutShift {
  mojo_base.mojom.TimeTicks layout_shift_time;
  double layout_shift_score;
};

// Metrics about how a RenderFrame rendered since the last UpdateTiming call.
struct FrameRenderDataUpdate {
  // How much visible elements in the frame shifted (https://bit.ly/3fQz29y) since
  // the last timing update.
  float layout_shift_delta;

  // How much visible elements in the frame shifted (https://bit.ly/3fQz29y),
  // before a user input or document scroll, since the last timing update.
  float layout_shift_delta_before_input_or_scroll;

  // New layout shifts with timestamps.
  array<LayoutShift> new_layout_shifts;
};

// Metrics about the time spent in tasks (cpu time) by a frame.
struct CpuTiming {
  // Time spent in tasks measured in wall time.
  mojo_base.mojom.TimeDelta task_time;
};

// Metrics about general input delay.
struct InputTiming {

  // The number of user interactions, including click, tap and key press.
  uint64 num_interactions = 0;

  // Includes an array of max_event_duration which is the longest input event
  // duration within a user interaction. The unit of duration is ms. Currently,
  // we send this array to the browser process behind a Finch flag.
  // TODO(crbug.com/40198645): Once the experiment is done, we could simplify
  // this part.
  UserInteractionLatencies max_event_durations;
};

// Data for user interaction latencies which can be meausred in different ways.
// Currently, the array user_interaction_latencies is sent behind a Finch flag.
// If we don't send the array, the maximum value will be sent instead.
union UserInteractionLatencies {
  array<UserInteractionLatency> user_interaction_latencies;
  // The maximum value in user_interaction_latencies.
  mojo_base.mojom.TimeDelta worst_interaction_latency;
};

// The latency and the type of a user interaction.
struct UserInteractionLatency {
  mojo_base.mojom.TimeDelta interaction_latency;
  UserInteractionType interaction_type;
  // The one-based offset of the interaction in time-based order; 1 for the
  // first interaction, 2 for the second, etc.
  uint64 interaction_offset;
  // The time the interaction occurred, relative to navigation start.
  mojo_base.mojom.TimeTicks interaction_time;
};

// The type of a user interaction, including keyboard, click or tap and drag.
enum UserInteractionType {
  kKeyboard,
  kTapOrClick,
  kDrag,
};

// Sent from renderer to browser process when the PageLoadTiming for the
// associated frame changed.
interface PageLoadMetrics {
  // Called when an update is ready to be sent from renderer to browser.
  // UpdateTiming calls are buffered, and contain all updates that have been
  // received in the last buffer window. Some of the update data may be empty.
  // Only called when at least one change has been observed within the frame.
  // Note that counts in `subresource_load_metrics` are cumulative and not a
  // delta.
  UpdateTiming(PageLoadTiming page_load_timing,
               FrameMetadata frame_metadata,
               // `new_features` will not contain any previously seen values.
               array<blink.mojom.UseCounterFeature> new_features,
               array<ResourceDataUpdate> resources,
               FrameRenderDataUpdate render_data,
               CpuTiming cpu_load_timing,
               InputTiming input_timing_delta,
               SubresourceLoadMetrics? subresource_load_metrics,
               SoftNavigationMetrics soft_navigation_metrics);

  // Set up a shared memory used to transfer smoothness data from the renderer
  // to the browser. The structure is defined in
  // //cc/metrics/ukm_smoothness_data.h
  SetUpSharedMemoryForSmoothness(
      mojo_base.mojom.ReadOnlySharedMemoryRegion shared_memory);

  // Called when performance entries are added via `performance.mark()` from the
  // outermost main frame in renderer, except for the case when standard
  // UserTiming marks `mark_fully_loaded`, `mark_fully_visible`, and
  // `mark_interactive` occur. Unlike `UpdateTiming`, this calls are not
  // buffered, and expects it always contains the user timing entry which is not
  // sent yet before.
  //
  // `AddCustomUserTiming` should be used when the expected transport data size
  // can be larger. Otherwise `UpdateTiming` is recommended.
  AddCustomUserTiming(CustomUserTimingMark custom_user_timing);
};

// TimeDelta below relative to the navigation start of the navigation restoring
// page from the back- forward cache.
struct BackForwardCacheTiming {
  // Time when the first paint is performed after the time when the page
  // is restored from the back-forward cache.
  mojo_base.mojom.TimeDelta first_paint_after_back_forward_cache_restore;

  // Times on requestAnimationFrame when the page is restored from the back-
  // forward cache.
  array<mojo_base.mojom.TimeDelta> request_animation_frames_after_back_forward_cache_restore;

  // Queueing Time of the first click, tap, key press, cancellable touchstart,
  // or pointer down followed by a pointer up after the time when the page is
  // restored from the back-forward cache.
  mojo_base.mojom.TimeDelta? first_input_delay_after_back_forward_cache_restore;
};

// Metrics about soft navigation. See soft navigation at https://github.com/WICG/soft-navigations.
// The metrics are to be added in a SoftNavigation Event which is parallel to
// a PageLoad event.
struct SoftNavigationMetrics {
  // The number of soft navigations that occur during a page load.
  uint64 count;

  // The start time of a soft navigation. It is the time the user initializes
  // the soft navigation. It is relative to navigation start.
  mojo_base.mojom.TimeDelta start_time;

  // The renderer side navigation id that increments when a soft navigation
  // happens.
  string navigation_id;

  // The LargestContentfulPaint of a soft navigation, that is, the largest
  // contentful paint that happened after a soft navigation is detected and
  // before the next soft navigation is detected or page end.
  // The timings are relative to the soft navigation start.
  LargestContentfulPaintTiming largest_contentful_paint;
};