File: page_load_metrics_update_dispatcher.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (423 lines) | stat: -rw-r--r-- 18,079 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
// 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.

#ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_UPDATE_DISPATCHER_H_
#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_UPDATE_DISPATCHER_H_

#include <map>
#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/page_load_metrics/browser/layout_shift_normalization.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer_delegate.h"
#include "components/page_load_metrics/browser/responsiveness_metrics_normalization.h"
#include "components/page_load_metrics/common/page_load_metrics.mojom.h"

namespace content {
class NavigationHandle;
class RenderFrameHost;
}  // namespace content

namespace page_load_metrics {

class PageLoadMetricsEmbedderInterface;

namespace internal {

enum class PageLoadTrackerPageType;

// Used to track the status of PageLoadTimings received from the render process.
//
// These values are recorded in histograms. Entries should not be renumbered
// and numeric values should never be reused.
//
// If you add elements to this enum, make sure you update the enum value in
// histograms.xml. Only add elements to the end to prevent inconsistencies
// between versions.
// LINT.IfChange(PageLoadTimingStatus)
enum PageLoadTimingStatus {
  // The PageLoadTiming is valid (all data within the PageLoadTiming is
  // consistent with expectations).
  VALID = 0,

  // All remaining status codes are for invalid PageLoadTimings.

  // The PageLoadTiming was empty.
  INVALID_EMPTY_TIMING = 1,

  // The PageLoadTiming had a null navigation_start.
  INVALID_NULL_NAVIGATION_START = 2,

  // Script load or execution durations in the PageLoadTiming were too long.
  INVALID_SCRIPT_LOAD_LONGER_THAN_PARSE = 3,
  INVALID_SCRIPT_EXEC_LONGER_THAN_PARSE = 4,
  INVALID_SCRIPT_LOAD_DOC_WRITE_LONGER_THAN_SCRIPT_LOAD = 5,
  INVALID_SCRIPT_EXEC_DOC_WRITE_LONGER_THAN_SCRIPT_EXEC = 6,

  // The order of two events in the PageLoadTiming was invalid. Either the first
  // wasn't present when the second was present, or the second was reported as
  // happening before the first.
  INVALID_ORDER_RESPONSE_START_PARSE_START = 7,
  INVALID_ORDER_PARSE_START_PARSE_STOP = 8,
  INVALID_ORDER_PARSE_STOP_DOM_CONTENT_LOADED = 9,
  INVALID_ORDER_DOM_CONTENT_LOADED_LOAD = 10,
  INVALID_ORDER_PARSE_START_FIRST_PAINT = 11,
  // Deprecated but not removing because it would affect histogram enumeration.
  INVALID_ORDER_FIRST_PAINT_FIRST_TEXT_PAINT = 12,
  INVALID_ORDER_FIRST_PAINT_FIRST_IMAGE_PAINT = 13,
  INVALID_ORDER_FIRST_PAINT_FIRST_CONTENTFUL_PAINT = 14,
  INVALID_ORDER_FIRST_PAINT_FIRST_MEANINGFUL_PAINT = 15,
  // Deprecated but not removing because it would affect histogram enumeration.
  INVALID_ORDER_FIRST_MEANINGFUL_PAINT_PAGE_INTERACTIVE = 16,

  // We received a first input delay without a first input timestamp.
  INVALID_NULL_FIRST_INPUT_TIMESTAMP = 17,
  // We received a first input timestamp without a first input delay.
  INVALID_NULL_FIRST_INPUT_DELAY = 18,

  // We received a longest input delay without a longest input timestamp.
  INVALID_NULL_LONGEST_INPUT_TIMESTAMP = 19,
  // We received a longest input timestamp without a longest input delay.
  INVALID_NULL_LONGEST_INPUT_DELAY = 20,

  // We received a first scroll delay without a first scroll timestamp.
  INVALID_NULL_FIRST_SCROLL_TIMESTAMP = 21,
  // We received a first scroll timestamp without a first scroll delay.
  INVALID_NULL_FIRST_SCROLL_DELAY = 22,

  // Longest input delay cannot happen before first input delay.
  INVALID_LONGEST_INPUT_TIMESTAMP_LESS_THAN_FIRST_INPUT_TIMESTAMP = 23,

  // Longest input delay cannot be less than first input delay.
  INVALID_LONGEST_INPUT_DELAY_LESS_THAN_FIRST_INPUT_DELAY = 24,

  // Deprecated but not removing because it would affect histogram enumeration.
  INVALID_ORDER_PARSE_START_ACTIVATION_START = 25,
  INVALID_ORDER_ACTIVATION_START_FIRST_PAINT = 26,

  // New values should be added before this final entry.
  LAST_PAGE_LOAD_TIMING_STATUS,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/page/enums.xml:PageLoadTimingStatus)

extern const char kPageLoadTimingStatus[];

}  // namespace internal

// PageLoadMetricsUpdateDispatcher manages updates to page load metrics data,
// and dispatches them to the Client. PageLoadMetricsUpdateDispatcher may delay
// dispatching metrics updates to the Client in cases where metrics state hasn't
// stabilized.
class PageLoadMetricsUpdateDispatcher {
 public:
  // The Client class is updated when metrics managed by the dispatcher have
  // changed. Typically it owns the dispatcher.
  class Client {
   public:
    virtual ~Client() = default;

    virtual PrerenderingState GetPrerenderingState() const = 0;
    virtual bool IsPageMainFrame(content::RenderFrameHost* rfh) const = 0;
    virtual void OnTimingChanged() = 0;
    virtual void OnPageInputTimingChanged(uint64_t num_interactions) = 0;
    virtual void OnSubFrameTimingChanged(
        content::RenderFrameHost* rfh,
        const mojom::PageLoadTiming& timing) = 0;
    virtual void OnMainFrameMetadataChanged() = 0;
    virtual void OnSubframeMetadataChanged(
        content::RenderFrameHost* rfh,
        const mojom::FrameMetadata& metadata) = 0;
    virtual void OnSubFrameInputTimingChanged(
        content::RenderFrameHost* rfh,
        const mojom::InputTiming& input_timing_delta) = 0;
    virtual void OnPageRenderDataChanged(
        const mojom::FrameRenderDataUpdate& render_data,
        bool is_main_frame) = 0;
    virtual void OnSubFrameRenderDataChanged(
        content::RenderFrameHost* rfh,
        const mojom::FrameRenderDataUpdate& render_data) = 0;
    virtual void OnSoftNavigationChanged(
        const mojom::SoftNavigationMetrics& soft_navigation_metrics) = 0;
    virtual void UpdateFeaturesUsage(
        content::RenderFrameHost* rfh,
        const std::vector<blink::UseCounterFeature>& new_features) = 0;
    virtual void UpdateResourceDataUse(
        content::RenderFrameHost* rfh,
        const std::vector<mojom::ResourceDataUpdatePtr>& resources) = 0;
    virtual void UpdateFrameCpuTiming(content::RenderFrameHost* rfh,
                                      const mojom::CpuTiming& timing) = 0;
    virtual void OnMainFrameIntersectionRectChanged(
        content::RenderFrameHost* rfh,
        const gfx::Rect& main_frame_intersection_rect) = 0;
    virtual void OnMainFrameViewportRectChanged(
        const gfx::Rect& main_frame_viewport_rect) = 0;
    virtual void OnMainFrameImageAdRectsChanged(
        const base::flat_map<int, gfx::Rect>& main_frame_image_ad_rects) = 0;
    virtual void SetUpSharedMemoryForUkms(
        base::ReadOnlySharedMemoryRegion smoothness_memory,
        base::ReadOnlySharedMemoryRegion dropped_frames_memory) = 0;
  };

  // The |client| instance must outlive this object.
  PageLoadMetricsUpdateDispatcher(
      Client* client,
      content::NavigationHandle* navigation_handle,
      PageLoadMetricsEmbedderInterface* embedder_interface);

  PageLoadMetricsUpdateDispatcher(const PageLoadMetricsUpdateDispatcher&) =
      delete;
  PageLoadMetricsUpdateDispatcher& operator=(
      const PageLoadMetricsUpdateDispatcher&) = delete;

  ~PageLoadMetricsUpdateDispatcher();

  void UpdateMetrics(content::RenderFrameHost* render_frame_host,
                     mojom::PageLoadTimingPtr new_timing,
                     mojom::FrameMetadataPtr new_metadata,
                     const std::vector<blink::UseCounterFeature>& new_features,
                     const std::vector<mojom::ResourceDataUpdatePtr>& resources,
                     mojom::FrameRenderDataUpdatePtr render_data,
                     mojom::CpuTimingPtr new_cpu_timing,
                     mojom::InputTimingPtr input_timing_delta,
                     const std::optional<blink::SubresourceLoadMetrics>&
                         subresource_load_metrics,
                     mojom::SoftNavigationMetricsPtr soft_navigation_metrics,
                     internal::PageLoadTrackerPageType page_type);

  void SetUpSharedMemoryForUkms(
      content::RenderFrameHost* render_frame_host,
      base::ReadOnlySharedMemoryRegion smoothness_memory,
      base::ReadOnlySharedMemoryRegion dropped_frames_memory);

  // This method is only intended to be called for PageLoadFeatures being
  // recorded directly from the browser process. Features coming from the
  // renderer process should use the main flow into |UpdateMetrics|.
  void UpdateFeatures(
      content::RenderFrameHost* render_frame_host,
      const std::vector<blink::UseCounterFeature>& new_features);

  void DidFinishSubFrameNavigation(
      content::NavigationHandle* navigation_handle);

  void OnSubFrameDeleted(content::FrameTreeNodeId frame_tree_node_id);

  void ShutDown();

  const mojom::PageLoadTiming& timing() const {
    return *(current_merged_page_timing_.get());
  }

  const mojom::FrameMetadata& main_frame_metadata() const {
    return *(main_frame_metadata_.get());
  }
  const mojom::FrameMetadata& subframe_metadata() const {
    return *(subframe_metadata_.get());
  }
  const PageRenderData& page_render_data() const { return page_render_data_; }
  const NormalizedCLSData& normalized_cls_data(
      PageLoadMetricsObserverDelegate::BfcacheStrategy bfcache_strategy) const {
    return bfcache_strategy ==
                   PageLoadMetricsObserverDelegate::BfcacheStrategy::RESET
               ? layout_shift_normalization_for_bfcache_.normalized_cls_data()
               : layout_shift_normalization_.normalized_cls_data();
  }
  const ResponsivenessMetricsNormalization&
  responsiveness_metrics_normalization() const {
    return responsiveness_metrics_normalization_;
  }

  const ResponsivenessMetricsNormalization&
  soft_navigation_interval_responsiveness_metrics_normalization() const {
    return soft_navigation_interval_responsiveness_metrics_normalization_;
  }

  const NormalizedCLSData& soft_navigation_interval_normalized_layout_shift()
      const {
    return soft_nav_interval_layout_shift_normalization_.normalized_cls_data();
  }

  void ResetSoftNavigationIntervalResponsivenessMetricsNormalization() {
    soft_navigation_interval_responsiveness_metrics_normalization_
        .ClearAllUserInteractionLatencies();
  }

  const PageRenderData& main_frame_render_data() const {
    return main_frame_render_data_;
  }
  const mojom::InputTiming& page_input_timing() const {
    return *page_input_timing_;
  }
  const std::optional<blink::SubresourceLoadMetrics>& subresource_load_metrics()
      const {
    return subresource_load_metrics_;
  }
  void UpdateResponsivenessMetricsNormalizationForBfcache() {
    responsiveness_metrics_normalization_.ClearAllUserInteractionLatencies();
  }
  void UpdateLayoutShiftNormalizationForBfcache() {
    cumulative_layout_shift_score_for_bfcache_ =
        page_render_data_.layout_shift_score;
    layout_shift_normalization_for_bfcache_.ClearAllLayoutShifts();
  }

  void ResetSoftNavigationIntervalLayoutShift() {
    soft_nav_interval_render_data_.layout_shift_score = 0;
    soft_nav_interval_render_data_.layout_shift_score_before_input_or_scroll =
        0;
    soft_nav_interval_layout_shift_normalization_.ClearAllLayoutShifts();
  }

  // Ensures all pending updates will get dispatched.
  void FlushPendingTimingUpdates();

 private:
  void UpdateMainFrameTiming(mojom::PageLoadTimingPtr new_timing,
                             internal::PageLoadTrackerPageType page_type);
  void UpdateSubFrameTiming(content::RenderFrameHost* render_frame_host,
                            mojom::PageLoadTimingPtr new_timing);
  void UpdateFrameCpuTiming(content::RenderFrameHost* render_frame_host,
                            mojom::CpuTimingPtr new_timing);
  void UpdateSubFrameInputTiming(content::RenderFrameHost* render_frame_host,
                                 const mojom::InputTiming& input_timing_delta);

  void UpdateMainFrameMetadata(content::RenderFrameHost* render_frame_host,
                               mojom::FrameMetadataPtr new_metadata);
  void UpdateSubFrameMetadata(content::RenderFrameHost* render_frame_host,
                              mojom::FrameMetadataPtr subframe_metadata);

  void UpdateMainFrameSubresourceLoadMetrics(
      const blink::SubresourceLoadMetrics& subresource_load_metrics);

  void UpdateSoftNavigation(
      const mojom::SoftNavigationMetrics& soft_navigation_metrics);

  void UpdateSoftNavigationIntervalResponsivenessMetrics(
      const mojom::InputTiming& input_timing_delta);

  void UpdateSoftNavigationIntervalLayoutShift(
      const mojom::FrameRenderDataUpdate& render_data);

  void UpdatePageInputTiming(const mojom::InputTiming& input_timing_delta);

  void MaybeUpdateMainFrameIntersectionRect(
      content::RenderFrameHost* render_frame_host,
      const mojom::FrameMetadataPtr& frame_metadata);
  void MaybeUpdateMainFrameViewportRect(
      const mojom::FrameMetadataPtr& frame_metadata);

  void UpdatePageRenderData(const mojom::FrameRenderDataUpdate& render_data,
                            bool is_main_frame);
  void UpdateMainFrameRenderData(
      const mojom::FrameRenderDataUpdate& render_data);
  void OnSubFrameRenderDataChanged(
      content::RenderFrameHost* render_frame_host,
      const mojom::FrameRenderDataUpdate& render_data);

  void MaybeDispatchTimingUpdates(bool did_merge_new_timing_value);
  void DispatchTimingUpdates();

  void UpdateHasSeenInputOrScroll(const mojom::PageLoadTiming& new_timing);

  // The client is guaranteed to outlive this object.
  const raw_ptr<Client> client_;

  // Interface to chrome features. Must outlive the class.
  const raw_ptr<PageLoadMetricsEmbedderInterface> embedder_interface_;

  std::unique_ptr<base::OneShotTimer> timer_;

  // Time the navigation for this page load was initiated.
  const base::TimeTicks navigation_start_;

  // PageLoadTiming for the currently tracked page. Some fields, such as FCP,
  // are merged across all frames in the document, while other fields are from
  // the main frame only (see PageLoadTimingMerger).
  //
  // |current_merged_page_timing_| contains the most recent valid timing data,
  // while |pending_merged_page_timing_| contains pending updates received since
  // |current_merged_page_timing_| was last dispatched to the client (see
  // DispatchTimingUpdates, which invokes the Client::OnTimingChanged callback).
  //
  mojom::PageLoadTimingPtr current_merged_page_timing_;
  mojom::PageLoadTimingPtr pending_merged_page_timing_;

  // TODO(crbug.com/40677945): Replace aggregate frame metadata with a separate
  // struct instead of using mojo.
  mojom::FrameMetadataPtr main_frame_metadata_;
  mojom::FrameMetadataPtr subframe_metadata_;

  // InputTiming data accumulated across all frames.
  mojom::InputTimingPtr page_input_timing_;

  // SubresourceLoadMetrics for the main frame.
  std::optional<blink::SubresourceLoadMetrics> subresource_load_metrics_;

  // True if this page load started in prerender.
  const bool is_prerendered_page_load_;

  // In general, page_render_data_ contains combined data across all frames on
  // the page, while main_frame_render_data_ contains data specific to the main
  // frame.
  //
  // The layout_shift_score_before_input_or_scroll field in page_render_data_
  // represents CLS across all frames (with subframe weighting), measured until
  // first input/scroll in any frame (including an OOPIF).
  //
  // The main frame layout_shift_score_before_input_or_scroll represents CLS
  // occurring within the main frame, measured until the first input/scroll seen
  // by the main frame (or an input sent to a same-site subframe, due to
  // crbug.com/1136207).
  //
  PageRenderData page_render_data_;
  PageRenderData main_frame_render_data_;

  PageRenderData soft_nav_interval_render_data_;

  // The last main frame intersection rects dispatched to page load metrics
  // observers.
  std::map<content::FrameTreeNodeId, gfx::Rect> main_frame_intersection_rects_;

  // The last main frame viewport rect dispatched to page load metrics
  // observers.
  std::optional<gfx::Rect> main_frame_viewport_rect_;

  LayoutShiftNormalization layout_shift_normalization_;
  LayoutShiftNormalization soft_nav_interval_layout_shift_normalization_;

  // Layout shift normalization data for bfcache which needs to be reset each
  // time the page enters the BackForward cache.
  LayoutShiftNormalization layout_shift_normalization_for_bfcache_;
  float cumulative_layout_shift_score_for_bfcache_ = 0.0;

  // Navigation start offsets for the most recently committed document in each
  // frame.
  std::map<content::FrameTreeNodeId, base::TimeDelta>
      subframe_navigation_start_offset_;

  // Whether we have seen an input or scroll event in any frame. This comes to
  // us via PaintTimingDetector::OnInputOrScroll, which triggers on user scrolls
  // and most input types (but not mousemove or pinch zoom). More comments in
  // UpdateHasSeenInputOrScroll.
  bool has_seen_input_or_scroll_ = false;

  // Where we receive user interaction latencies from all renderer frames and
  // calculate a few normalized responsiveness metrics. It will be reset every
  // time the page enters bfcache.
  ResponsivenessMetricsNormalization responsiveness_metrics_normalization_;

  // Keeps track of user interaction latencies on main frame for soft
  // navigation intervals. A soft navigation interval is either the
  // interval from page load start to 1st soft navigation, or an interval
  // between 2 soft navigations, or the interval from the last soft navigation
  // to the page load end.
  ResponsivenessMetricsNormalization
      soft_navigation_interval_responsiveness_metrics_normalization_;
};

}  // namespace page_load_metrics

#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_UPDATE_DISPATCHER_H_