// 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 "mojo/public/mojom/base/byte_size.mojom";
import "mojo/public/mojom/base/shared_memory.mojom";
import "mojo/public/mojom/base/time.mojom";
import "mojo/public/mojom/base/unguessable_token.mojom";
import "services/network/public/mojom/request_priority.mojom";
import "third_party/blink/public/mojom/loader/javascript_framework_detection.mojom";
import "third_party/blink/public/mojom/navigation/navigation_type_for_navigation_api.mojom";
import "third_party/blink/public/mojom/use_counter/use_counter_feature.mojom";
import "ui/gfx/geometry/mojom/geometry.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;

  // The monotonic ordinal navigation ID of the corresponding soft navigation;
  // 1 for initial navigation.
  uint64 performance_timeline_navigation_id;
};

// 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;
};

// `MonotonicPaintTiming` contains the full-resolution `TimeTicks` versions of a
// subset of `PaintTiming` fields.
// It is used when the browser needs to calculate a duration between a paint
// event and another event timestamped in the browser, e.g. application launch.
// The `TimeDelta` versions in `PaintTiming` are relative to navigation start,
// and both of them are converted in a way that reconstructing the original
// `TimeTicks` from them can be lossy. These raw `TimeTicks` values allow for
// more accurate duration calculations.
struct MonotonicPaintTiming {
  // Time when the first paint is performed.
  mojo_base.mojom.TimeTicks? first_paint;

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

  // Time when the first contentful paint was submitted to compositor.
  mojo_base.mojom.TimeTicks? first_contentful_paint_submitted;
};

// 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.com/41247575 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.com/41247575 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;
};

// Represents Unicode scripts (and Emojis) that we track for font fallback.
// This is a curated subset of ICU's UScriptCode (plus kEmoji) designed
// specifically for tracking high-impact font fallback categories in UMA.
// LINT.IfChange(ScriptType)
enum ScriptType {
  // Catch-all for scripts not explicitly listed below.
  kOther = 0,
  // Latin script (USCRIPT_LATIN).
  kLatin = 1,
  // Han (CJK Ideographs) script (USCRIPT_HAN).
  kHan = 2,
  // Hangul script (USCRIPT_HANGUL).
  kHangul = 3,
  // Hiragana script (USCRIPT_HIRAGANA).
  kHiragana = 4,
  // Katakana script (USCRIPT_KATAKANA).
  kKatakana = 5,
  // Arabic script (USCRIPT_ARABIC).
  kArabic = 6,
  // Bengali script (USCRIPT_BENGALI).
  kBengali = 7,
  // Devanagari script (USCRIPT_DEVANAGARI).
  kDevanagari = 8,
  // Cyrillic script (USCRIPT_CYRILLIC).
  kCyrillic = 9,
  // Common script (USCRIPT_COMMON), used for math, symbols, etc.
  kCommon = 10,
  // Emojis (detected via fallback priority instead of script code).
  kEmoji = 11,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/page/histograms.xml:GWSFontFallbackScript)

struct ScriptFallbackInfo {
  // The script type (or Emoji) of the fallback character.
  ScriptType script_type;
  // The number of fallback events for this script/Emoji.
  uint32 fallback_count;
};

struct FontLoadingMetrics {
  // Total cumulative time spent in character fallback.
  mojo_base.mojom.TimeDelta? fallback_duration;

  // Total number of character fallback events.
  uint32 fallback_count;

  // Duration of the first fallback event in the process.
  mojo_base.mojom.TimeDelta? fallback_initial_duration;

  // NGShapeCache hits.
  uint32 shape_cache_hit_count;

  // NGShapeCache misses.
  uint32 shape_cache_miss_count;

  // Detailed fallback metrics per script.
  array<ScriptFallbackInfo> script_fallback_metrics;
};

// 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 network connection was
  // established.
  mojo_base.mojom.TimeDelta? connect_end;

  // 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;

  // The full-resolution monotonic times of the selected subset of
  // `paint_timing`.
  MonotonicPaintTiming? monotonic_paint_timing;

  // 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;

  // The main frame's document rectangle, originating at (0,0) and encapsulating
  // the document's dimensions. Set only for the outermost main frame when
  // initially computed or upon subsequent changes. Null if unchanged.
  gfx.mojom.Rect? main_frame_rect;

  // The main frame's viewport rectangle (encapsulating dimensions and
  // scroll position) in the main frame's coordinate system. Set only for the
  // outermost main frame when initially computed or upon subsequent changes.
  // Null if unchanged.
  gfx.mojom.Rect? main_frame_viewport_rect;

  // A map of ad element node IDs to their bounding rectangles in the main
  // frame's coordinate system. An empty rectangle signals the removal of an
  // element. Populated only for the outermost main frame when a rectangle is
  // initially computed or subsequently changed.
  map<int32, gfx.mojom.Rect> main_frame_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 number of sub-resources which were matched to the
  // `RouterSourceEnum.race-network-and-cache` in ServiceWorker Static
  // Routing API, from navigation start till onload event.
  uint32 matched_race_network_and_cache_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;

  // Total cache lookup time of ServiceWorker Static Routing API for
  // sub-resources.
  mojo_base.mojom.TimeDelta total_cache_lookup_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.
  mojo_base.mojom.ByteSize delta_bytes;

  // Total network bytes received for the resource across timing updates. This
  // is the aggregate of the |delta_bytes| from each timing update.
  mojo_base.mojom.ByteSize received_data_length;

  // The length of the response body for the resource before removing any
  // content encodings. Only set for complete resources.
  mojo_base.mojom.ByteSize encoded_body_length;

  // The length of the response body in bytes for the resource after decoding.
  // Only set for complete resources.
  mojo_base.mojom.ByteSize decoded_body_length;

  // 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.
// See https://github.com/WICG/layout-instability/blob/main/README.md
// and https://web.dev/articles/cls.
struct LayoutShift {
  mojo_base.mojom.TimeTicks layout_shift_time;
  double layout_shift_score;
  // Whether this layout shift occurred after a user input or document scroll.
  bool after_input_or_scroll;
  // The performance timeline navigation ID to which this layout shift is
  // attributed. 1 for the initial/hard navigation, 2, 3, ... for subsequent
  // soft navigations.
  uint64 performance_timeline_navigation_id;
};

// Metrics about how a RenderFrame rendered since the last UpdateTiming call.
struct FrameRenderDataUpdate {
  // 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;
};

// The latency and the type of a user interaction.
struct EventTiming {
  mojo_base.mojom.TimeDelta duration;
  // The one-based offset of the interaction in time-based order; 1 for the
  // first interaction, 2 for the second, etc.
  uint64 interaction_id;
  // The time the interaction occurred.
  mojo_base.mojom.TimeTicks start_time;
  // The time the event processing on the main thread started.
  mojo_base.mojom.TimeTicks processing_start;
  // The performance timeline navigation ID to which this interaction is
  // attributed. 1 for the initial/hard navigation, 2, 3, ... for subsequent
  // soft navigations.
  uint64 performance_timeline_navigation_id;
};

// 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,
               array<EventTiming> event_timings,
               SubresourceLoadMetrics? subresource_load_metrics,
               array<SoftNavigationMetrics> soft_navigation_metrics,
               array<LargestContentfulPaintTiming> soft_largest_contentful_paint,
               array<CustomUserTimingMark> custom_user_timings,
               FontLoadingMetrics? font_loading_metrics);

  // 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.
//
// Immutable initial commit metadata for a soft navigation, sent once upon
// commit.
struct SoftNavigationCommit {
  // The start time of a soft navigation. It is the time the user initializes
  // the soft navigation. It is relative to navigation start, and is the time
  // origin for calculating the loading performance of the soft navigation,
  // e.g. LCP.
  mojo_base.mojom.TimeDelta start_time;

  // Monotonically increasing TimeTicks timestamp recorded at the time of
  // the soft navigation commit in the renderer. Used to validate monotonic
  // ordering of incoming soft navigation commits.
  mojo_base.mojom.TimeTicks soft_navigation_slicing_time;

  // The type of the soft navigation.
  blink.mojom.NavigationTypeForNavigationApi navigation_type;

  // Identifies the same document navigation for the initial URL change.
  // This allows us to map to the UKM Source ID in the browser side,
  // and therefore attribute the metrics to the correct URL
  // when any corresponding soft navigation completes.
  mojo_base.mojom.UnguessableToken same_document_metrics_token;
};

// Sent in `PageLoadMetrics.UpdateTiming` to report soft navigation lifecycle
// updates. This represents either the initial soft navigation commit
// (with `commit` set, and optional `first_contentful_paint` if already
// presented), or a subsequent update (such as late `first_contentful_paint`).
struct SoftNavigationMetrics {
  // A unique monotonic ordinal number assigned to this soft navigation from
  // the start of the page load (2, 3, ... n). 1 is reserved for the initial
  // page load.
  uint64 performance_timeline_navigation_id;

  // Initial commit metadata. Present when this message conveys the soft
  // navigation commit.
  SoftNavigationCommit? commit;

  // Presentation time of the soft navigation's First Contentful Paint relative
  // to navigation start, if available.
  mojo_base.mojom.TimeDelta? first_contentful_paint;
};
