Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(193)

Side by Side Diff: chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc

Issue 2798953002: [PageLoadMetrics] Keep track of Ad Sizes on Pages (Closed)
Patch Set: Address comments from PS8 Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/page_load_metrics/observers/ads_page_load_metrics_obser ver.h"
6
7 #include <string>
8
9 #include "base/feature_list.h"
10 #include "base/strings/string_util.h"
11 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
12 #include "content/public/browser/navigation_handle.h"
13 #include "content/public/browser/render_frame_host.h"
14 #include "content/public/browser/web_contents.h"
15 #include "url/gurl.h"
16
17 namespace {
18
19 const base::Feature kAdsFeature{"AdsMetrics", base::FEATURE_ENABLED_BY_DEFAULT};
20
21 bool FrameIsAd(content::NavigationHandle* navigation_handle) {
22 int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
23 content::RenderFrameHost* current_frame_host =
24 navigation_handle->GetWebContents()->FindFrameByFrameTreeNodeId(
25 frame_tree_node_id);
26 const std::string& name = current_frame_host->GetFrameName();
27 const GURL& url = navigation_handle->GetURL();
28
29 // Because sub-resource filtering isn't always enabled, and doesn't work
30 // well in monitoring mode (no CSS enforcement), it's difficult to identify
31 // ads. Google ads are prevalent and easy to track, so we'll start by
32 // tracking those. Note that the frame name can be very large, so be careful
33 // to avoid full string searches if possible.
34 // TODO(jkarlin): Track other ad networks that are easy to identify.
35 return base::StartsWith(name, "google_ads_iframe",
36 base::CompareCase::SENSITIVE) ||
37 base::StartsWith(name, "google_ads_frame",
38 base::CompareCase::SENSITIVE) ||
39 (url.host_piece() == "tpc.googlesyndication.com" &&
40 base::StartsWith(url.path_piece(), "/safeframe",
41 base::CompareCase::SENSITIVE));
42 }
43
44 } // namespace
45
46 AdsPageLoadMetricsObserver::AdsPageLoadMetricsObserver() = default;
47 AdsPageLoadMetricsObserver::~AdsPageLoadMetricsObserver() = default;
48
49 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
50 AdsPageLoadMetricsObserver::OnCommit(
51 content::NavigationHandle* navigation_handle) {
52 DCHECK(ad_frames_data_.empty());
53
54 if (!base::FeatureList::IsEnabled(kAdsFeature))
55 return STOP_OBSERVING;
Charlie Harrison 2017/04/11 17:30:06 Can you do this feature detection at construction
jkarlin 2017/04/13 17:25:30 Done.
56
57 // The main frame is never considered an ad.
58 ad_frames_data_[navigation_handle->GetFrameTreeNodeId()] = nullptr;
59 ProcessDelayedResources(navigation_handle->GetFrameTreeNodeId());
60 return CONTINUE_OBSERVING;
61 }
62
63 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
64 AdsPageLoadMetricsObserver::OnCommitSubFrame(
65 content::NavigationHandle* navigation_handle) {
66 DCHECK(!base::ContainsKey(ad_frames_data_,
67 navigation_handle->GetFrameTreeNodeId()));
68
69 FrameTreeNodeId frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
70 AdFrameData* ancestor_data = FindAdAncestor(navigation_handle);
71
72 if (ancestor_data) {
73 // This frame is nested within a known ad frame.
74 ad_frames_data_[frame_tree_node_id] = ancestor_data;
75 } else if (FrameIsAd(navigation_handle)) {
76 // This is the first ad frame in the frame's ancestry.
77 ad_frames_data_storage_.push_back(AdFrameData());
78 ad_frames_data_[frame_tree_node_id] = &ad_frames_data_storage_.back();
79 } else {
80 // No ads in the frame's ancestry, including itself.
81 ad_frames_data_[frame_tree_node_id] = nullptr;
82 }
83
84 ProcessDelayedResources(frame_tree_node_id);
85 return CONTINUE_OBSERVING;
86 }
87
88 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
89 AdsPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
90 const page_load_metrics::PageLoadTiming& timing,
91 const page_load_metrics::PageLoadExtraInfo& extra_info) {
92 // The browser may come back, but there is no guarantee. To be safe, record
93 // what we have now and ignore future changes to this navigation.
94 if (extra_info.did_commit)
95 RecordHistograms();
96
97 return STOP_OBSERVING;
98 }
99
100 void AdsPageLoadMetricsObserver::OnLoadedResource(
101 const page_load_metrics::ExtraRequestInfo& extra_request_info) {
102 const auto& id_and_data =
103 ad_frames_data_.find(extra_request_info.frame_tree_node_id);
104 if (id_and_data == ad_frames_data_.end()) {
105 // This resouce is for a frame that hasn't yet committed. It must be the
106 // main document for the frame. Hold onto it and once it commits we'll run
107 // it in ProcessDelayedResources.
108 auto it_and_success = delayed_resources_.insert(std::make_pair(
109 extra_request_info.frame_tree_node_id, extra_request_info));
110 DCHECK(it_and_success.second);
111 return;
112 }
113
114 page_bytes_ += extra_request_info.raw_body_bytes;
115 if (!extra_request_info.was_cached)
116 uncached_page_bytes_ += extra_request_info.raw_body_bytes;
117
118 // Determine if the frame (or its ancestor) is an ad, if so attribute the
119 // bytes to the highest ad ancestor.
120 AdFrameData* ancestor_data = id_and_data->second;
121
122 if (ancestor_data) {
123 ancestor_data->frame_bytes += extra_request_info.raw_body_bytes;
124 if (!extra_request_info.was_cached) {
125 ancestor_data->frame_bytes_uncached += extra_request_info.raw_body_bytes;
126 }
127 }
128 }
129
130 void AdsPageLoadMetricsObserver::OnComplete(
131 const page_load_metrics::PageLoadTiming& timing,
132 const page_load_metrics::PageLoadExtraInfo& info) {
133 RecordHistograms();
134 }
135
136 void AdsPageLoadMetricsObserver::RecordHistograms() {
137 if (page_bytes_ == 0)
138 return;
139
140 size_t total_ad_frame_bytes = 0;
141 size_t uncached_ad_frame_bytes = 0;
142 int ad_frames = 0;
143
144 for (const AdFrameData& ad_frame_data : ad_frames_data_storage_) {
145 total_ad_frame_bytes += ad_frame_data.frame_bytes;
146 uncached_ad_frame_bytes += ad_frame_data.frame_bytes_uncached;
147
148 if (ad_frame_data.frame_bytes > 0) {
149 ad_frames += 1;
150 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.AdFrame",
151 ad_frame_data.frame_bytes);
152 PAGE_BYTES_HISTOGRAM(
153 "PageLoad.Clients.Ads.Google.Bytes.AdFrameFromNetwork",
154 ad_frame_data.frame_bytes_uncached);
155 UMA_HISTOGRAM_PERCENTAGE(
156 "PageLoad.Clients.Ads.Google.BytesPercent.AdFrameFromNetwork",
157 ad_frame_data.frame_bytes_uncached * 100 / ad_frame_data.frame_bytes);
158 }
159 }
160
161 // Don't post UMA for pages that don't have ads or content.
162 if (total_ad_frame_bytes == 0) {
163 UMA_HISTOGRAM_COUNTS("PageLoad.Clients.Ads.Google.PageHasNoAds", 1);
164 return;
165 }
166
167 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.AdFrames",
168 total_ad_frame_bytes);
169
170 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.PageSansAdFrames",
171 page_bytes_ - total_ad_frame_bytes);
172
173 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.Page", page_bytes_);
174 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.PageFromNetwork",
175 uncached_page_bytes_);
176
177 UMA_HISTOGRAM_PERCENTAGE("PageLoad.Clients.Ads.Google.BytesPercent.AdFrames",
178 total_ad_frame_bytes * 100 / page_bytes_);
179
180 UMA_HISTOGRAM_COUNTS_1000("PageLoad.Clients.Ads.Google.AdFrameCount",
181 ad_frames);
182
183 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.AdFramesFromNetwork",
184 uncached_ad_frame_bytes);
185
186 UMA_HISTOGRAM_PERCENTAGE(
187 "PageLoad.Clients.Ads.Google.BytesPercent."
188 "AdFramesFromNetworkOfAdFramesTotal",
189 uncached_ad_frame_bytes * 100 / total_ad_frame_bytes);
190
191 int percent_bytes_from_uncached_ads =
192 uncached_page_bytes_ == 0
193 ? 0
194 : uncached_ad_frame_bytes * 100 / uncached_page_bytes_;
195 UMA_HISTOGRAM_PERCENTAGE(
196 "PageLoad.Clients.Ads.Google.Bytes.Percent."
197 "AdFramesFromNetworkOfPageFromNetwork",
198 percent_bytes_from_uncached_ads);
199 }
200
201 AdsPageLoadMetricsObserver::AdFrameData*
202 AdsPageLoadMetricsObserver::FindAdAncestor(
203 content::NavigationHandle* navigation_handle) {
204 // We haven't seen a load from this frame before. We should have seen its
205 // parent though. Use the ad ancestor of its parent.
206 content::RenderFrameHost* parent_frame_host =
207 navigation_handle->GetRenderFrameHost()->GetParent();
208 DCHECK(parent_frame_host); // Since this isn't a main frame.
209 const auto& frame_id_and_data =
210 ad_frames_data_.find(parent_frame_host->GetFrameTreeNodeId());
211 DCHECK(frame_id_and_data != ad_frames_data_.end());
212 return frame_id_and_data->second;
213 }
214
215 void AdsPageLoadMetricsObserver::ProcessDelayedResources(
216 FrameTreeNodeId frame_tree_node_id) {
217 const auto& frame_id_and_request =
218 delayed_resources_.find(frame_tree_node_id);
219 if (frame_id_and_request == delayed_resources_.end())
220 return;
221 OnLoadedResource(frame_id_and_request->second);
222 delayed_resources_.erase(frame_id_and_request);
223 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698