blob: 0f85a8b5bb084effda7dc9f26ee537b3efb98724 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_FRAME_DATA_H_
#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_FRAME_DATA_H_
#include "base/macros.h"
#include "base/optional.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
#include "components/page_load_metrics/common/page_load_metrics.mojom.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
// Store information received for a frame on the page. FrameData is meant
// to represent a frame along with it's entire subtree.
class FrameData {
public:
// 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,
};
// Whether or not the ad frame has a display: none styling.
enum FrameVisibility {
kNonVisible = 0,
kVisible = 1,
kAnyVisibility = 2,
kMaxValue = kAnyVisibility,
};
// 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,
};
// 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,
};
// The interactive states for the main page, which describe when the page
// has reached interactive state, or finished loading.
enum class InteractiveStatus {
kPreInteractive = 0,
kPostInteractive = 1,
kMaxValue = kPostInteractive,
};
// High level categories of mime types for resources loaded by the frame.
enum class ResourceMimeType {
kJavascript = 0,
kVideo = 1,
kImage = 2,
kCss = 3,
kHtml = 4,
kOther = 5,
kMaxValue = kOther,
};
// 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,
};
// Window over which to consider cpu time spent in an ad_frame.
static constexpr base::TimeDelta kCpuWindowSize =
base::TimeDelta::FromSeconds(30);
using FrameTreeNodeId =
page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId;
// Get the mime type of a resource. This only returns a subset of mime types,
// grouped at a higher level. For example, all video mime types return the
// same value.
static ResourceMimeType GetResourceMimeType(
const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
explicit FrameData(FrameTreeNodeId frame_tree_node_id,
int heavy_ad_network_threshold_noise);
~FrameData();
// Update the metadata of this frame if it is being navigated.
void UpdateForNavigation(content::RenderFrameHost* render_frame_host,
bool frame_navigated);
// Updates the number of bytes loaded in the frame given a resource load.
void ProcessResourceLoadInFrame(
const page_load_metrics::mojom::ResourceDataUpdatePtr& resource,
int process_id,
const page_load_metrics::ResourceTracker& resource_tracker);
// Adds additional bytes to the ad resource byte counts. This
// is used to notify the frame that some bytes were tagged as ad bytes after
// they were loaded.
void AdjustAdBytes(int64_t unaccounted_ad_bytes, ResourceMimeType mime_type);
// 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);
// Add the cpu |update| appropriately given the page |interactive| status.
void UpdateCpuUsage(base::TimeTicks update_time,
base::TimeDelta update,
InteractiveStatus interactive);
// Returns whether the heavy ad intervention was triggered on this frame.
// This intervention is triggered when the frame is considered heavy, has not
// received user gesture, and the intervention feature is enabled. This
// returns true the first time the criteria is met, and false afterwards.
bool MaybeTriggerHeavyAdIntervention();
// Get the cpu usage for the appropriate interactive period.
base::TimeDelta GetInteractiveCpuUsage(InteractiveStatus status) const;
// Get the cpu usage for the appropriate activation period.
base::TimeDelta GetActivationCpuUsage(UserActivationStatus status) const;
// Get total cpu usage for the frame.
base::TimeDelta GetTotalCpuUsage() const;
// Records that the sticky user activation bit has been set on the frame.
// Cannot be unset. Also records the page foreground duration at that time.
void SetReceivedUserActivation(base::TimeDelta foreground_duration);
// Get the unactivated duration for this frame.
base::TimeDelta pre_activation_foreground_duration() const {
return pre_activation_foreground_duration_;
}
// Updates the max frame depth of this frames tree given the newly seen child
// frame.
void MaybeUpdateFrameDepth(content::RenderFrameHost* render_frame_host);
// 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;
int peak_windowed_cpu_percent() const { return peak_windowed_cpu_percent_; }
base::Optional<base::TimeTicks> peak_window_start_time() const {
return peak_window_start_time_;
}
FrameTreeNodeId frame_tree_node_id() const { return frame_tree_node_id_; }
OriginStatus origin_status() const { return origin_status_; }
size_t bytes() const { return bytes_; }
size_t network_bytes() const { return network_bytes_; }
size_t same_origin_bytes() const { return same_origin_bytes_; }
size_t ad_bytes() const { return ad_bytes_; }
size_t ad_network_bytes() const { return ad_network_bytes_; }
size_t GetAdNetworkBytesForMime(ResourceMimeType mime_type) const;
UserActivationStatus user_activation_status() const {
return user_activation_status_;
}
bool frame_navigated() const { return frame_navigated_; }
FrameVisibility visibility() const { return visibility_; }
gfx::Size frame_size() const { return frame_size_; }
MediaStatus media_status() const { return media_status_; }
void set_media_status(MediaStatus media_status) {
media_status_ = media_status;
}
void set_timing(page_load_metrics::mojom::PageLoadTimingPtr timing) {
timing_ = std::move(timing);
}
HeavyAdStatus heavy_ad_status() const { return heavy_ad_status_; }
HeavyAdStatus heavy_ad_status_with_noise() const {
return heavy_ad_status_with_noise_;
}
private:
// Time updates for the frame with a timestamp indicating when they arrived.
// Used for windowed cpu load reporting.
struct CpuUpdateData {
base::TimeTicks update_time;
base::TimeDelta usage_info;
CpuUpdateData(base::TimeTicks time, base::TimeDelta info)
: update_time(time), usage_info(info) {}
};
// 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.
HeavyAdStatus ComputeHeavyAdStatus(bool use_network_threshold_noise) const;
// The most recently updated timing received for this frame.
page_load_metrics::mojom::PageLoadTimingPtr timing_;
// Number of resources loaded by the frame (both complete and incomplete).
int num_resources_ = 0;
// Total bytes used to load resources in the frame, including headers.
size_t bytes_;
size_t network_bytes_;
// Records ad network bytes for different mime type resources loaded in the
// frame.
size_t ad_bytes_by_mime_[static_cast<size_t>(ResourceMimeType::kMaxValue) +
1] = {0};
// Time spent by the frame in the cpu before and after interactive.
base::TimeDelta cpu_by_interactive_period_[static_cast<size_t>(
InteractiveStatus::kMaxValue) +
1] = {base::TimeDelta(),
base::TimeDelta()};
// Time spent by the frame in the cpu before and after activation.
base::TimeDelta cpu_by_activation_period_
[static_cast<size_t>(UserActivationStatus::kMaxValue) + 1] = {
base::TimeDelta(), base::TimeDelta()};
// Duration of time the page spent in the foreground before activation.
base::TimeDelta pre_activation_foreground_duration_;
// The cpu time spent in the current window.
base::TimeDelta cpu_total_for_current_window_;
// The cpu updates themselves that are still relevant for the time window.
// Note: Since the window is 30 seconds and PageLoadMetrics updates arrive at
// most every half second, this can never have more than 60 elements.
base::queue<CpuUpdateData> cpu_updates_for_current_window_;
// The peak windowed cpu load during the unactivated period.
int peak_windowed_cpu_percent_ = 0;
// The time that the peak cpu usage window started at.
base::Optional<base::TimeTicks> peak_window_start_time_;
// The depth of this FrameData's root frame.
unsigned int root_frame_depth_ = 0;
// The max depth of this frames frame tree.
unsigned int frame_depth_ = 0;
// Tracks the number of bytes that were used to load resources which were
// detected to be ads inside of this frame. For ad frames, these counts should
// match |frame_bytes| and |frame_network_bytes|.
size_t ad_bytes_ = 0u;
size_t ad_network_bytes_ = 0u;
// The number of bytes that are same origin to the root ad frame.
size_t same_origin_bytes_;
const FrameTreeNodeId frame_tree_node_id_;
OriginStatus origin_status_;
bool frame_navigated_;
UserActivationStatus user_activation_status_;
bool is_display_none_;
FrameVisibility visibility_;
gfx::Size frame_size_;
url::Origin origin_;
MediaStatus media_status_ = MediaStatus::kNotPlayed;
// Indicates whether or not this frame met the criteria for the heavy ad
// intervention.
HeavyAdStatus heavy_ad_status_;
// Same as |heavy_ad_status_| but uses 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_;
// Number of bytes of noise that should be added to the network threshold.
const int heavy_ad_network_threshold_noise_;
DISALLOW_COPY_AND_ASSIGN(FrameData);
};
#endif // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_FRAME_DATA_H_