Commit 939b83b6 authored by John Delaney's avatar John Delaney Committed by Commit Bot

Record UseCounter for adframe size intervention

Record how many pages and how many ad iframes would be affected by a
size intervention on frames without user activation, that exceed a size
on 1050 kilobytes. This defines size as the sum of all cached resource
sizes and all network bytes used to load a frame and all of its
subresources.

Bug: 878393
Change-Id: I4ea1ba03f6b348f83a02d7f22650e1b38a46a28e
Reviewed-on: https://chromium-review.googlesource.com/c/1440912
Commit-Queue: John Delaney <johnidel@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Reviewed-by: default avatarMustafa Emre Acer <meacer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#630060}
parent 21576dbb
...@@ -121,6 +121,7 @@ AdsPageLoadMetricsObserver::OnCommit( ...@@ -121,6 +121,7 @@ AdsPageLoadMetricsObserver::OnCommit(
DCHECK(ad_frames_data_.empty()); DCHECK(ad_frames_data_.empty());
committed_ = true; committed_ = true;
web_contents_ = navigation_handle->GetWebContents();
// The main frame is never considered an ad. // The main frame is never considered an ad.
ad_frames_data_[navigation_handle->GetFrameTreeNodeId()] = nullptr; ad_frames_data_[navigation_handle->GetFrameTreeNodeId()] = nullptr;
...@@ -432,8 +433,16 @@ void AdsPageLoadMetricsObserver::ProcessResourceForFrame( ...@@ -432,8 +433,16 @@ void AdsPageLoadMetricsObserver::ProcessResourceForFrame(
// Determine if the frame (or its ancestor) is an ad, if so attribute the // Determine if the frame (or its ancestor) is an ad, if so attribute the
// bytes to the highest ad ancestor. // bytes to the highest ad ancestor.
FrameData* ancestor_data = id_and_data->second; FrameData* ancestor_data = id_and_data->second;
if (ancestor_data) if (!ancestor_data)
ancestor_data->ProcessResourceLoadInFrame(resource); return;
ancestor_data->ProcessResourceLoadInFrame(resource);
if (web_contents_ && ancestor_data->size_intervention_status() ==
FrameData::FrameSizeInterventionStatus::kTriggered) {
RecordSingleFeatureUsage(
web_contents_->GetMainFrame(),
blink::mojom::WebFeature::kAdFrameSizeIntervention);
}
} }
AdsPageLoadMetricsObserver::ResourceMimeType AdsPageLoadMetricsObserver::ResourceMimeType
...@@ -639,6 +648,9 @@ void AdsPageLoadMetricsObserver::RecordHistogramsForAdTagging( ...@@ -639,6 +648,9 @@ void AdsPageLoadMetricsObserver::RecordHistogramsForAdTagging(
UMA_HISTOGRAM_COUNTS_10000, visibility, UMA_HISTOGRAM_COUNTS_10000, visibility,
std::min(ad_frame_data.frame_size().width(), std::min(ad_frame_data.frame_size().width(),
ad_frame_data.frame_size().height())); ad_frame_data.frame_size().height()));
ADS_HISTOGRAM("FrameCounts.AdFrames.PerFrame.SizeIntervention",
UMA_HISTOGRAM_ENUMERATION, visibility,
ad_frame_data.size_intervention_status());
non_zero_ad_frames += 1; non_zero_ad_frames += 1;
total_ad_frame_bytes += ad_frame_data.frame_bytes(); total_ad_frame_bytes += ad_frame_data.frame_bytes();
......
...@@ -38,6 +38,15 @@ class AdsPageLoadMetricsObserver ...@@ -38,6 +38,15 @@ class AdsPageLoadMetricsObserver
kMaxValue = kOther, kMaxValue = kOther,
}; };
// Whether or not the adframe size intervention would have triggered on
// this frame. These values are persisted to logs. Entries should not be
// renumbered and numeric values should never be reused.
enum class AdFrameSizeInterventionStatus {
kNone = 0,
kTriggered = 1,
kMaxValue = kTriggered,
};
// Returns a new AdsPageLoadMetricObserver. If the feature is disabled it // Returns a new AdsPageLoadMetricObserver. If the feature is disabled it
// returns nullptr. // returns nullptr.
static std::unique_ptr<AdsPageLoadMetricsObserver> CreateIfNeeded(); static std::unique_ptr<AdsPageLoadMetricsObserver> CreateIfNeeded();
...@@ -166,6 +175,9 @@ class AdsPageLoadMetricsObserver ...@@ -166,6 +175,9 @@ class AdsPageLoadMetricsObserver
std::map<int, page_load_metrics::mojom::ResourceDataUpdatePtr> std::map<int, page_load_metrics::mojom::ResourceDataUpdatePtr>
page_resources_; page_resources_;
// The web contents associated with this page load.
content::WebContents* web_contents_ = nullptr;
// Tallies for bytes and counts observed in resource data updates for the // Tallies for bytes and counts observed in resource data updates for the
// entire page. // entire page.
size_t page_ad_javascript_bytes_ = 0u; size_t page_ad_javascript_bytes_ = 0u;
......
...@@ -60,6 +60,10 @@ const char kSmallestDimensionHistogramId[] = ...@@ -60,6 +60,10 @@ const char kSmallestDimensionHistogramId[] =
"PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame." "PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame."
"SmallestDimension"; "SmallestDimension";
const char kAdFrameSizeInterventionHistogramId[] =
"PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame."
"SizeIntervention";
enum class Origin { enum class Origin {
kNavigation, kNavigation,
kAnchorAttribute, kAnchorAttribute,
...@@ -737,6 +741,114 @@ IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest, ...@@ -737,6 +741,114 @@ IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
"PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame.Total", 2, 1); "PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame.Total", 2, 1);
} }
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
AdFrameSizeInterventionTriggered) {
base::HistogramTester histogram_tester;
SetRulesetWithRules(
{subresource_filter::testing::CreateSuffixRule("ad_iframe_writer.js")});
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/ads_observer");
content::SetupCrossSiteRedirector(embedded_test_server());
const char kHttpResponseHeader[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n";
auto resource_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/incomplete_resource.js",
true /*relative_url_is_prefix*/);
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreateAdsPageLoadMetricsTestWaiter();
browser()->OpenURL(content::OpenURLParams(
embedded_test_server()->GetURL("/ad_with_incomplete_resource.html"),
content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false));
waiter->AddMinimumCompleteResourcesExpectation(3);
waiter->Wait();
// Load a resource large enough to trigger intervention.
resource_response->WaitForRequest();
resource_response->Send(kHttpResponseHeader);
resource_response->Send(
std::string(FrameData::kFrameSizeInterventionByteThreshold, ' '));
resource_response->Done();
// Wait for the resource to finish loading.
waiter->AddMinimumCompleteResourcesExpectation(4);
waiter->Wait();
// Close all tabs to report metrics.
browser()->tab_strip_model()->CloseAllTabs();
histogram_tester.ExpectBucketCount(
kAdFrameSizeInterventionHistogramId,
FrameData::FrameSizeInterventionStatus::kTriggered, 1);
histogram_tester.ExpectBucketCount(
"Blink.UseCounter.Features",
blink::mojom::WebFeature::kAdFrameSizeIntervention, 1);
}
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
AdFrameSizeInterventionNotActivatedOnFrameWithGesture) {
base::HistogramTester histogram_tester;
SetRulesetWithRules(
{subresource_filter::testing::CreateSuffixRule("ad_iframe_writer.js")});
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/ads_observer");
content::SetupCrossSiteRedirector(embedded_test_server());
const char kHttpResponseHeader[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n";
auto resource_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/incomplete_resource.js",
true /*relative_url_is_prefix*/);
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreateAdsPageLoadMetricsTestWaiter();
browser()->OpenURL(content::OpenURLParams(
embedded_test_server()->GetURL("/ad_with_incomplete_resource.html"),
content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false));
waiter->AddMinimumCompleteResourcesExpectation(3);
waiter->Wait();
// Activate one frame by executing a dummy script.
content::RenderFrameHost* ad_frame =
ChildFrameAt(web_contents()->GetMainFrame(), 0);
const std::string no_op_script = "// No-op script";
EXPECT_TRUE(ExecuteScript(ad_frame, no_op_script));
// Load a resource large enough to trigger intervention.
resource_response->WaitForRequest();
resource_response->Send(kHttpResponseHeader);
resource_response->Send(
std::string(FrameData::kFrameSizeInterventionByteThreshold, ' '));
resource_response->Done();
// Wait for the resource to finish loading.
waiter->AddMinimumCompleteResourcesExpectation(4);
waiter->Wait();
// Close all tabs to report metrics.
browser()->tab_strip_model()->CloseAllTabs();
histogram_tester.ExpectBucketCount(
kAdFrameSizeInterventionHistogramId,
FrameData::FrameSizeInterventionStatus::kNone, 1);
histogram_tester.ExpectBucketCount(
"Blink.UseCounter.Features",
blink::mojom::WebFeature::kAdFrameSizeIntervention, 0);
}
// Verify that per-resource metrics are reported for cached resources and // Verify that per-resource metrics are reported for cached resources and
// resources loaded by the network. // resources loaded by the network.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest, IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
......
...@@ -26,7 +26,8 @@ FrameData::FrameData(FrameTreeNodeId frame_tree_node_id) ...@@ -26,7 +26,8 @@ FrameData::FrameData(FrameTreeNodeId frame_tree_node_id)
user_activation_status_(UserActivationStatus::kNoActivation), user_activation_status_(UserActivationStatus::kNoActivation),
is_display_none_(false), is_display_none_(false),
visibility_(FrameVisibility::kVisible), visibility_(FrameVisibility::kVisible),
frame_size_(gfx::Size()) {} frame_size_(gfx::Size()),
size_intervention_status_(FrameSizeInterventionStatus::kNone) {}
FrameData::~FrameData() = default; FrameData::~FrameData() = default;
...@@ -56,6 +57,11 @@ void FrameData::ProcessResourceLoadInFrame( ...@@ -56,6 +57,11 @@ void FrameData::ProcessResourceLoadInFrame(
// Report cached resource body bytes to overall frame bytes. // Report cached resource body bytes to overall frame bytes.
if (resource->is_complete && resource->was_fetched_via_cache) if (resource->is_complete && resource->was_fetched_via_cache)
frame_bytes_ += resource->encoded_body_length; frame_bytes_ += resource->encoded_body_length;
if (frame_bytes_ > kFrameSizeInterventionByteThreshold &&
user_activation_status_ == UserActivationStatus::kNoActivation) {
size_intervention_status_ = FrameSizeInterventionStatus::kTriggered;
}
} }
void FrameData::SetFrameSize(gfx::Size frame_size) { void FrameData::SetFrameSize(gfx::Size frame_size) {
......
...@@ -32,6 +32,15 @@ class FrameData { ...@@ -32,6 +32,15 @@ class FrameData {
kMaxValue = kAnyVisibility, kMaxValue = kAnyVisibility,
}; };
// Whether or not the frame size intervention would have triggered on
// this frame. These values are persisted to logs. Entries should not be
// renumbered and numeric values should never be reused.
enum class FrameSizeInterventionStatus {
kNone = 0,
kTriggered = 1,
kMaxValue = kTriggered,
};
// These values are persisted to logs. Entries should not be renumbered and // These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused. For any additions, also update the // numeric values should never be reused. For any additions, also update the
// corresponding PageEndReason enum in enums.xml. // corresponding PageEndReason enum in enums.xml.
...@@ -41,6 +50,9 @@ class FrameData { ...@@ -41,6 +50,9 @@ class FrameData {
kMaxValue = kReceivedActivation, kMaxValue = kReceivedActivation,
}; };
// Maximum number of bytes allowed to be loaded by a frame.
static const int kFrameSizeInterventionByteThreshold = 1050 * 1024;
using FrameTreeNodeId = using FrameTreeNodeId =
page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId; page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId;
...@@ -85,6 +97,10 @@ class FrameData { ...@@ -85,6 +97,10 @@ class FrameData {
gfx::Size frame_size() const { return frame_size_; } gfx::Size frame_size() const { return frame_size_; }
FrameSizeInterventionStatus size_intervention_status() const {
return size_intervention_status_;
}
private: private:
// Updates whether or not this frame meets the criteria for visibility. // Updates whether or not this frame meets the criteria for visibility.
void UpdateFrameVisibility(); void UpdateFrameVisibility();
...@@ -100,6 +116,10 @@ class FrameData { ...@@ -100,6 +116,10 @@ class FrameData {
FrameVisibility visibility_; FrameVisibility visibility_;
gfx::Size frame_size_; gfx::Size frame_size_;
// Indicates whether or not this frame would have triggered a size
// intervention.
FrameSizeInterventionStatus size_intervention_status_;
DISALLOW_COPY_AND_ASSIGN(FrameData); DISALLOW_COPY_AND_ASSIGN(FrameData);
}; };
......
...@@ -2221,6 +2221,7 @@ enum WebFeature { ...@@ -2221,6 +2221,7 @@ enum WebFeature {
kV8RemotePlayback_Prompt_Method = 2781, kV8RemotePlayback_Prompt_Method = 2781,
kLayoutJankExplicitlyRequested = 2782, kLayoutJankExplicitlyRequested = 2782,
kMediaSessionSkipAd = 2783, kMediaSessionSkipAd = 2783,
kAdFrameSizeIntervention = 2784,
// Add new features immediately above this line. Don't change assigned // Add new features immediately above this line. Don't change assigned
// numbers of any item, and don't reuse removed slots. // numbers of any item, and don't reuse removed slots.
......
...@@ -576,6 +576,11 @@ uploading your change for review. These are checked by presubmit scripts. ...@@ -576,6 +576,11 @@ uploading your change for review. These are checked by presubmit scripts.
label="Timeout during installability check, site status unknown"/> label="Timeout during installability check, site status unknown"/>
</enum> </enum>
<enum name="AdFrameSizeInterventionStatus">
<int value="0" label="None"/>
<int value="1" label="Triggered"/>
</enum>
<enum name="AdFrameVisibility"> <enum name="AdFrameVisibility">
<int value="0" label="Display none"/> <int value="0" label="Display none"/>
<int value="1" label="Visible"/> <int value="1" label="Visible"/>
...@@ -21525,6 +21530,7 @@ Called by update_net_error_codes.py.--> ...@@ -21525,6 +21530,7 @@ Called by update_net_error_codes.py.-->
<int value="2781" label="V8RemotePlayback_Prompt_Method"/> <int value="2781" label="V8RemotePlayback_Prompt_Method"/>
<int value="2782" label="LayoutJankExplicitlyRequested"/> <int value="2782" label="LayoutJankExplicitlyRequested"/>
<int value="2783" label="MediaSessionSkipAd"/> <int value="2783" label="MediaSessionSkipAd"/>
<int value="2784" label="AdFrameSizeIntervention"/>
</enum> </enum>
<enum name="FeaturePolicyFeature"> <enum name="FeaturePolicyFeature">
...@@ -78302,6 +78302,20 @@ uploading your change for review. ...@@ -78302,6 +78302,20 @@ uploading your change for review.
</summary> </summary>
</histogram> </histogram>
<histogram base="true"
name="PageLoad.FrameCounts.AdFrames.PerFrame.SizeIntervention"
enum="AdFrameSizeInterventionStatus" expires_after="2020-01-29">
<owner>johnidel@chromium.org</owner>
<owner>jkarlin@chromium.org</owner>
<summary>
Whether or not the ad frame would have triggered the Adframe size
intervention. This is determined by checking if a frame does not have a user
gesture when its size exceeds 1050 kilobytes. Only recorded for ad frames
with non-zero total bytes. Recorded for each frame when the page is
destroyed on navigated.
</summary>
</histogram>
<histogram base="true" <histogram base="true"
name="PageLoad.FrameCounts.AdFrames.PerFrame.SmallestDimension" name="PageLoad.FrameCounts.AdFrames.PerFrame.SmallestDimension"
units="pixels" expires_after="2020-01-30"> units="pixels" expires_after="2020-01-30">
...@@ -131821,6 +131835,8 @@ uploading your change for review. ...@@ -131821,6 +131835,8 @@ uploading your change for review.
<affected-histogram name="PageLoad.Bytes"/> <affected-histogram name="PageLoad.Bytes"/>
<affected-histogram <affected-histogram
name="PageLoad.FrameCounts.AdFrames.PerFrame.OriginStatus"/> name="PageLoad.FrameCounts.AdFrames.PerFrame.OriginStatus"/>
<affected-histogram
name="PageLoad.FrameCounts.AdFrames.PerFrame.SizeIntervention"/>
<affected-histogram <affected-histogram
name="PageLoad.FrameCounts.AdFrames.PerFrame.SmallestDimension"/> name="PageLoad.FrameCounts.AdFrames.PerFrame.SmallestDimension"/>
<affected-histogram <affected-histogram
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment