File: frame_tree_data.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 (373 lines) | stat: -rw-r--r-- 13,959 bytes parent folder | download | duplicates (5)
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
// Copyright 2018 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_OBSERVERS_AD_METRICS_FRAME_TREE_DATA_H_
#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_AD_METRICS_FRAME_TREE_DATA_H_

#include <stdint.h>

#include <array>
#include <optional>

#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/page_load_metrics/browser/observers/ad_metrics/frame_data_utils.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
#include "components/page_load_metrics/common/page_load_metrics.mojom-forward.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "ui/gfx/geometry/size.h"
#include "url/origin.h"

// Resource usage thresholds for the Heavy Ad Intervention feature. These
// numbers are platform specific and are intended to target 1 in 1000 ad iframes
// on each platform, for network and CPU use respectively.
namespace heavy_ad_thresholds {

// Maximum number of network bytes allowed to be loaded by a frame. These
// numbers reflect the 99.9th percentile of the
// PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame.Network histogram on mobile and
// desktop. Additive noise is added to this threshold, see
// AdsPageLoadMetricsObserver::HeavyAdThresholdNoiseProvider.
const int kMaxNetworkBytes = 4.0 * 1024 * 1024;

// CPU thresholds are selected from AdFrameLoad UKM, and are intended to target
// 1 in 1000 ad iframes combined, with each threshold responsible for roughly
// half of those intervention. Maximum number of milliseconds of CPU use allowed
// to be used by a frame.
const int kMaxCpuTime = 60 * 1000;

// Maximum percentage of CPU utilization over a 30 second window allowed.
const int kMaxPeakWindowedPercent = 50;

}  // namespace heavy_ad_thresholds

namespace page_load_metrics {

// The origin of the ad relative to the main frame's origin.
// Note: Logged to UMA, keep in sync with CrossOriginAdStatus in enums.xml.
//   Add new entries to the end, and do not renumber.
enum class OriginStatus {
  kUnknown = 0,
  kSame = 1,
  kCross = 2,
  kMaxValue = kCross,
};

// Origin status further broken down by whether the ad frame tree has a
// frame currently not render-throttled (i.e. is eligible to be painted).
// Note that since creative origin status is based on first contentful paint,
// only ad frame trees with unknown creative origin status can be without any
// frames that are eligible to be painted.
// Note: Logged to UMA, keep in sync with
// CrossOriginCreativeStatusWithThrottling in enums.xml.
// Add new entries to the end, and do not renumber.
enum class OriginStatusWithThrottling {
  kUnknownAndUnthrottled = 0,
  kUnknownAndThrottled = 1,
  kSameAndUnthrottled = 2,
  kCrossAndUnthrottled = 3,
  kMaxValue = kCrossAndUnthrottled,
};

// The type of heavy ad this frame is classified as per the Heavy Ad
// Intervention.
enum class HeavyAdStatus {
  kNone = 0,
  kNetwork = 1,
  kTotalCpu = 2,
  kPeakCpu = 3,
  kMaxValue = kPeakCpu,
};

// Controls what values of HeavyAdStatus will be cause an unload due to the
// intervention.
enum class HeavyAdUnloadPolicy {
  kNetworkOnly = 0,
  kCpuOnly = 1,
  kAll = 2,
};

// Represents how a frame should be treated by the heavy ad intervention.
enum class HeavyAdAction {
  // Nothing should be done, i.e. the ad is not heavy or the intervention is
  // not enabled.
  kNone = 0,
  // The ad should be reported as heavy.
  kReport = 1,
  // The ad should be reported and unloaded.
  kUnload = 2,
  // The frame was ignored, i.e. the blocklist was full or page is a reload.
  kIgnored = 3,
};

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused. For any additions, also update the
// corresponding PageEndReason enum in enums.xml.
enum class UserActivationStatus {
  kNoActivation = 0,
  kReceivedActivation = 1,
  kMaxValue = kReceivedActivation,
};

// Whether or not media has been played in this frame. These values are
// persisted to logs. Entries should not be renumbered and numeric values
// should never be reused.
enum class MediaStatus {
  kNotPlayed = 0,
  kPlayed = 1,
  kMaxValue = kPlayed,
};

// FrameTreeData represents a frame along with its entire subtree, and is
// typically used to capture an ad creative. It stores frame-specific
// information (such as size, activation status, and origin), which is typically
// specific to the top frame in the tree.
class FrameTreeData final {
 public:
  // |root_frame_tree_node_id| is the root frame of the subtree that
  // FrameTreeData stores information for.
  explicit FrameTreeData(content::FrameTreeNodeId root_frame_tree_node_id,
                         int heavy_ad_network_threshold_noise);
  ~FrameTreeData();

  // Processes a resource load in frame, calling ResourceLoadAggregator.
  void ProcessResourceLoadInFrame(const mojom::ResourceDataUpdatePtr& resource,
                                  int process_id,
                                  const ResourceTracker& resource_tracker);

  // Adjusts ad bytes after call to ProcessResourceLoadInFrame, calling
  // ResourceLoadAggregator.
  void AdjustAdBytes(int64_t unaccounted_ad_bytes, ResourceMimeType mime_type);

  // Updates the cpu usage of this frame.
  void UpdateCpuUsage(base::TimeTicks update_time, base::TimeDelta update);

  // Get the total cpu usage of this frame;
  base::TimeDelta GetTotalCpuUsage() const;

  // Update the metadata of this frame if it is being navigated.
  void UpdateForNavigation(content::RenderFrameHost* render_frame_host);

  // Returns how the frame should be treated by the heavy ad intervention.
  // This intervention is triggered when the frame is considered heavy, has not
  // received user gesture, and the intervention feature is enabled. This
  // returns an action the first time the criteria is met, and false afterwards.
  HeavyAdAction MaybeTriggerHeavyAdIntervention();

  // Updates the max frame depth of this frames tree given the newly seen child
  // frame.
  void MaybeUpdateFrameDepth(content::RenderFrameHost* render_frame_host);

  // Updates the recorded bytes of memory used.
  void UpdateMemoryUsage(int64_t delta_bytes);

  // Returns whether the frame should be recorded for UKMs and UMA histograms.
  // A frame should be recorded if it has non-zero bytes or non-zero CPU usage
  // (or both).
  bool ShouldRecordFrameForMetrics() const;

  // Construct and record an AdFrameLoad UKM event for this frame. Only records
  // events for frames that have non-zero bytes.
  void RecordAdFrameLoadUkmEvent(ukm::SourceId source_id) const;

  // Returns the corresponding enum value to split the creative origin status
  // by whether any frame in the ad frame tree is throttled.
  OriginStatusWithThrottling GetCreativeOriginStatusWithThrottling() const;

  // Get the cpu usage for the appropriate activation period.
  base::TimeDelta GetActivationCpuUsage(UserActivationStatus status) const {
    return cpu_usage_[static_cast<size_t>(status)];
  }

  content::FrameTreeNodeId root_frame_tree_node_id() const {
    return root_frame_tree_node_id_;
  }

  OriginStatus origin_status() const { return origin_status_; }

  OriginStatus creative_origin_status() const {
    return creative_origin_status_;
  }

  std::optional<base::TimeDelta> first_eligible_to_paint() const {
    return first_eligible_to_paint_;
  }

  std::optional<base::TimeDelta> earliest_first_contentful_paint() const {
    return earliest_first_contentful_paint_;
  }

  std::optional<base::TimeDelta> earliest_fcp_since_top_nav_start() const {
    return earliest_fcp_since_top_nav_start_;
  }

  // Sets the size of the frame and updates its visibility state.
  void SetFrameSize(gfx::Size frame_size_);

  // Sets the display state of the frame and updates its visibility state.
  void SetDisplayState(bool is_display_none);

  UserActivationStatus user_activation_status() const {
    return user_activation_status_;
  }

  FrameVisibility visibility() const { return visibility_; }

  gfx::Size frame_size() const { return frame_size_; }

  bool is_display_none() const { return is_display_none_; }

  MediaStatus media_status() const { return media_status_; }

  void set_media_status(MediaStatus media_status) {
    media_status_ = media_status;
  }

  // Records that the sticky user activation bit has been set on the frame.
  // Cannot be unset.
  void set_received_user_activation() {
    user_activation_status_ = UserActivationStatus::kReceivedActivation;
  }

  void set_creative_origin_status(OriginStatus creative_origin_status) {
    creative_origin_status_ = creative_origin_status;
  }

  void SetFirstEligibleToPaint(std::optional<base::TimeDelta> time_stamp);

  // Returns whether a new FCP is set.
  bool SetEarliestFirstContentfulPaint(
      std::optional<base::TimeDelta> time_stamp);

  void SetEarliestFirstContentfulPaintSinceTopNavStart(
      base::TimeDelta time_since_top_nav_start);

  HeavyAdStatus heavy_ad_status_with_noise() const {
    return heavy_ad_status_with_noise_;
  }

  HeavyAdStatus heavy_ad_status_with_policy() const {
    return heavy_ad_status_with_policy_;
  }

  void set_heavy_ad_action(HeavyAdAction heavy_ad_action) {
    heavy_ad_action_ = heavy_ad_action;
  }

  // Accessor for the total resource data of the frame tree.
  const ResourceLoadAggregator& resource_data() const { return resource_data_; }

  // Accessor for the peak windowed cpu usage of the frame tree.
  int peak_windowed_cpu_percent() const {
    return peak_cpu_.peak_windowed_percent();
  }

  base::WeakPtr<FrameTreeData> AsWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

 private:
  // Updates whether or not this frame meets the criteria for visibility.
  void UpdateFrameVisibility();

  // Computes whether this frame meets the criteria for being a heavy frame for
  // the heavy ad intervention and returns the type of threshold hit if any.
  // If |use_network_threshold_noise| is set,
  // |heavy_ad_network_threshold_noise_| is added to the network threshold when
  // computing the status. |policy| controls which thresholds are used when
  // computing the status.
  HeavyAdStatus ComputeHeavyAdStatus(bool use_network_threshold_noise,
                                     HeavyAdUnloadPolicy policy) const;

  // The frame tree node id of root frame of the subtree that |this| is
  // tracking information for.
  const content::FrameTreeNodeId root_frame_tree_node_id_;

  // TODO(ericrobinson): May want to move this to ResourceLoadAggregator.
  // Number of resources loaded by the frame (both complete and incomplete).
  int num_resources_ = 0;

  // The depth of this FrameTreeData's root frame.
  unsigned int root_frame_depth_ = 0;

  // The max depth of this frames frame tree.
  unsigned int frame_depth_ = 0;

  // The origin status of the ad frame for the creative.
  OriginStatus origin_status_ = OriginStatus::kUnknown;

  // The origin status of the creative content.
  OriginStatus creative_origin_status_ = OriginStatus::kUnknown;

  // Whether or not the frame is set to not display.
  bool is_display_none_ = false;

  // Whether or not a creative is large enough to be visible by the user.
  FrameVisibility visibility_ = FrameVisibility::kVisible;

  // The size of the frame.
  gfx::Size frame_size_;

  // Whether or not the frame has started media playing.
  MediaStatus media_status_ = MediaStatus::kNotPlayed;

  // Earliest time that any frame in the ad frame tree has reported
  // as being eligible to paint, or null if all frames are currently
  // render-throttled and there hasn't been a first paint. Note that this
  // timestamp and the implied throttling status are best-effort.
  std::optional<base::TimeDelta> first_eligible_to_paint_;

  // The smallest FCP seen for any any frame in this ad frame tree, if a
  // frame has painted.
  std::optional<base::TimeDelta> earliest_first_contentful_paint_;

  // The smallest FCP time seen for any frame in this ad frame tree less the
  // time from top-frame navigation start.
  std::optional<base::TimeDelta> earliest_fcp_since_top_nav_start_;

  // Indicates whether or not this frame met the criteria for the heavy ad
  // intervention with additional additive noise for the
  // network threshold. A frame can be considered a heavy ad by
  // |heavy_ad_status_| but not |heavy_ad_status_with_noise_|. The noised
  // threshold is used when determining whether to actually trigger the
  // intervention.
  HeavyAdStatus heavy_ad_status_with_noise_ = HeavyAdStatus::kNone;

  // Same as |heavy_ad_status_with_noise_| but selectively uses thresholds based
  // on a field trial param. This status is used to control when the
  // intervention fires.
  HeavyAdStatus heavy_ad_status_with_policy_ = HeavyAdStatus::kNone;

  // The action taken on this frame by the heavy ad intervention if any.
  HeavyAdAction heavy_ad_action_ = HeavyAdAction::kNone;

  // Number of bytes of noise that should be added to the network threshold.
  const int heavy_ad_network_threshold_noise_;

  // Whether or not the frame has been activated (clicked on).
  UserActivationStatus user_activation_status_ =
      UserActivationStatus::kNoActivation;

  // The cpu usage for both the activated and unactivated time periods.
  std::array<base::TimeDelta,
             static_cast<size_t>(UserActivationStatus::kMaxValue) + 1>
      cpu_usage_;

  // The resource data for this frame tree.
  ResourceLoadAggregator resource_data_;

  // The peak cpu usage for this frame tree.
  PeakCpuAggregator peak_cpu_;

  // Memory usage by v8 in this ad frame tree.
  MemoryUsageAggregator memory_usage_;

  // Owns weak pointers to the instance.
  base::WeakPtrFactory<FrameTreeData> weak_ptr_factory_{this};
};

}  // namespace page_load_metrics

#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_AD_METRICS_FRAME_TREE_DATA_H_