OLD | NEW |
---|---|
(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 "chrome/browser/page_load_metrics/page_load_metrics_util.h" | |
8 #include "content/public/browser/render_frame_host.h" | |
9 #include "content/public/browser/web_contents.h" | |
10 | |
11 namespace { | |
12 | |
13 bool FrameIsAd(content::NavigationHandle* navigation_handle) { | |
14 int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId(); | |
Charlie Harrison
2017/04/10 18:39:41
#include navigation_handle
jkarlin
2017/04/11 16:46:28
Done.
| |
15 content::RenderFrameHost* current_frame_host = | |
16 navigation_handle->GetWebContents()->FindFrameByFrameTreeNodeId( | |
17 frame_tree_node_id); | |
18 const std::string& name = current_frame_host->GetFrameName(); | |
Charlie Harrison
2017/04/10 18:39:41
#include <string>
jkarlin
2017/04/11 16:46:28
Done.
| |
19 const std::string& url_spec = navigation_handle->GetURL().spec(); | |
Charlie Harrison
2017/04/10 18:39:41
#include gurl
jkarlin
2017/04/11 16:46:28
Done.
| |
20 | |
21 // Because sub-resource filtering isn't always enabled, and doesn't work | |
22 // well in monitoring mode (no CSS enforcement), it's difficult to identify | |
23 // ads. Google ads are prevalent and easy to track, so we'll start by | |
24 // tracking those. | |
25 // TODO(jkarlin): Track other ad networks that are easy to identify. | |
26 return base::StartsWith(name, "google_ads_iframe", | |
Charlie Harrison
2017/04/10 18:39:41
#include base/strings/string_util.h
Charlie Harrison
2017/04/10 18:39:41
Can you document that frame names can be very big,
jkarlin
2017/04/11 16:46:27
Done.
jkarlin
2017/04/11 16:46:28
Done.
| |
27 base::CompareCase::SENSITIVE) || | |
28 base::StartsWith(name, "google_ads_frame", | |
29 base::CompareCase::SENSITIVE) || | |
30 base::StartsWith(url_spec, | |
Charlie Harrison
2017/04/10 18:39:41
Replace these last two with:
url.host_piece() == "
jkarlin
2017/04/11 16:46:28
Done.
| |
31 "http://tpc.googlesyndication.com/safeframe/", | |
32 base::CompareCase::SENSITIVE) || | |
33 base::StartsWith(url_spec, | |
34 "https://tpc.googlesyndication.com/safeframe/", | |
35 base::CompareCase::SENSITIVE); | |
36 } | |
37 | |
38 } // namespace | |
39 | |
40 AdsPageLoadMetricsObserver::AdsPageLoadMetricsObserver() = default; | |
41 AdsPageLoadMetricsObserver::~AdsPageLoadMetricsObserver() = default; | |
42 | |
43 page_load_metrics::PageLoadMetricsObserver::ObservePolicy | |
44 AdsPageLoadMetricsObserver::OnCommit( | |
45 content::NavigationHandle* navigation_handle) { | |
46 DCHECK(ad_frames_.empty()); | |
47 DCHECK(ad_ancestors_.empty()); | |
48 | |
49 // The main frame is never considered an ad. | |
50 ad_ancestors_[navigation_handle->GetFrameTreeNodeId()] = | |
51 kInvalidFrameTreeNodeId; | |
52 ProcessDelayedResources(navigation_handle->GetFrameTreeNodeId()); | |
53 return CONTINUE_OBSERVING; | |
54 } | |
55 | |
56 page_load_metrics::PageLoadMetricsObserver::ObservePolicy | |
57 AdsPageLoadMetricsObserver::OnCommitSubFrame( | |
58 content::NavigationHandle* navigation_handle) { | |
59 DCHECK( | |
60 !base::ContainsKey(ad_frames_, navigation_handle->GetFrameTreeNodeId())); | |
61 | |
62 FrameTreeNodeId frame_tree_node_id = navigation_handle->GetFrameTreeNodeId(); | |
63 FrameTreeNodeId ancestor_id = FindAdAncestor(navigation_handle); | |
64 | |
65 if (ancestor_id != kInvalidFrameTreeNodeId) { | |
Charlie Harrison
2017/04/10 18:39:41
optional: slightly prefer:
if (ancestor_id != kIn
jkarlin
2017/04/11 16:46:27
Done.
| |
66 ad_ancestors_[frame_tree_node_id] = ancestor_id; | |
67 ProcessDelayedResources(frame_tree_node_id); | |
68 return CONTINUE_OBSERVING; | |
69 } | |
70 | |
71 if (FrameIsAd(navigation_handle)) { | |
72 ad_frames_[frame_tree_node_id] = AdFrameData(); | |
73 ad_ancestors_[frame_tree_node_id] = frame_tree_node_id; | |
74 ProcessDelayedResources(frame_tree_node_id); | |
75 return CONTINUE_OBSERVING; | |
76 } | |
77 | |
78 ad_ancestors_[frame_tree_node_id] = kInvalidFrameTreeNodeId; | |
79 ProcessDelayedResources(frame_tree_node_id); | |
80 return CONTINUE_OBSERVING; | |
81 } | |
82 | |
83 page_load_metrics::PageLoadMetricsObserver::ObservePolicy | |
84 AdsPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground( | |
85 const page_load_metrics::PageLoadTiming& timing, | |
86 const page_load_metrics::PageLoadExtraInfo& extra_info) { | |
87 // The browser may come back, but there is no guarantee. To be safe, record | |
88 // what we have now and ignore future changes to this navigation. | |
89 if (extra_info.did_commit) | |
90 RecordHistograms(); | |
91 | |
92 return STOP_OBSERVING; | |
93 } | |
94 | |
95 void AdsPageLoadMetricsObserver::OnLoadedResource( | |
96 const page_load_metrics::ExtraRequestInfo& extra_request_info) { | |
97 if (!base::ContainsKey(ad_ancestors_, | |
Charlie Harrison
2017/04/10 18:39:41
Lots of map lookups per resource request :(
I won
jkarlin
2017/04/11 16:46:28
Done.
| |
98 extra_request_info.frame_tree_node_id)) { | |
99 // This resouce is for a frame that hasn't yet committed. It must be the | |
100 // main document for the frame. Hold onto it and once it commits we'll run | |
101 // it in ProcessDelayedResources. | |
102 auto it_and_success = delayed_resources_.insert(std::make_pair( | |
103 extra_request_info.frame_tree_node_id, extra_request_info)); | |
104 DCHECK(it_and_success.second); | |
105 return; | |
106 } | |
107 | |
108 page_bytes_ += extra_request_info.raw_body_bytes; | |
109 if (!extra_request_info.was_cached) | |
110 uncached_page_bytes_ += extra_request_info.raw_body_bytes; | |
111 | |
112 // Determine if the frame (or its ancestor) is an ad, if so attribute the | |
113 // bytes to the highest ad ancestor. | |
114 FrameTreeNodeId ad_ancestor_id = | |
115 ad_ancestors_[extra_request_info.frame_tree_node_id]; | |
116 if (ad_ancestor_id != kInvalidFrameTreeNodeId) { | |
117 ad_frames_[ad_ancestor_id].frame_bytes += extra_request_info.raw_body_bytes; | |
118 if (!extra_request_info.was_cached) | |
Charlie Harrison
2017/04/10 18:39:41
nit: needs {}
jkarlin
2017/04/11 16:46:28
Done.
| |
119 ad_frames_[ad_ancestor_id].frame_bytes_uncached += | |
120 extra_request_info.raw_body_bytes; | |
121 } | |
122 } | |
123 | |
124 void AdsPageLoadMetricsObserver::OnComplete( | |
125 const page_load_metrics::PageLoadTiming& timing, | |
126 const page_load_metrics::PageLoadExtraInfo& info) { | |
127 RecordHistograms(); | |
128 } | |
129 | |
130 void AdsPageLoadMetricsObserver::RecordHistograms() { | |
131 if (page_bytes_ == 0) | |
132 return; | |
133 | |
134 size_t total_ad_frame_bytes = 0; | |
135 size_t uncached_ad_frame_bytes = 0; | |
136 int ad_frames = 0; | |
137 | |
138 for (const auto& frame_id_and_size : ad_frames_) { | |
Charlie Harrison
2017/04/10 18:39:41
Is logging on the order of 100 histograms in a loo
jkarlin
2017/04/11 16:46:28
https://chromium.googlesource.com/chromium/src.git
| |
139 total_ad_frame_bytes += frame_id_and_size.second.frame_bytes; | |
140 uncached_ad_frame_bytes += frame_id_and_size.second.frame_bytes_uncached; | |
141 | |
142 const AdFrameData& data = frame_id_and_size.second; | |
143 if (data.frame_bytes > 0) { | |
144 ad_frames += 1; | |
145 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.AdFrame", | |
146 data.frame_bytes); | |
147 PAGE_BYTES_HISTOGRAM( | |
148 "PageLoad.Clients.Ads.Google.Bytes.AdFrameFromNetwork", | |
149 data.frame_bytes_uncached); | |
150 UMA_HISTOGRAM_PERCENTAGE( | |
151 "PageLoad.Clients.Ads.Google.BytesPercent.AdFrameFromNetwork", | |
152 data.frame_bytes_uncached * 100 / data.frame_bytes); | |
153 } | |
154 } | |
155 | |
156 // Don't post UMA for pages that don't have ads or content. | |
157 if (total_ad_frame_bytes == 0) { | |
158 UMA_HISTOGRAM_COUNTS("PageLoad.Clients.Ads.Google.PageHasNoAds", 1); | |
159 return; | |
160 } | |
161 | |
162 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.AdFrames", | |
163 total_ad_frame_bytes); | |
164 | |
165 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.PageSansAdFrames", | |
166 page_bytes_ - total_ad_frame_bytes); | |
167 | |
168 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.Page", page_bytes_); | |
169 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.PageFromNetwork", | |
170 uncached_page_bytes_); | |
171 | |
172 UMA_HISTOGRAM_PERCENTAGE("PageLoad.Clients.Ads.Google.BytesPercent.AdFrames", | |
173 total_ad_frame_bytes * 100 / page_bytes_); | |
174 | |
175 UMA_HISTOGRAM_COUNTS_1000("PageLoad.Clients.Ads.Google.AdFrameCount", | |
176 ad_frames); | |
177 | |
178 PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Google.Bytes.AdFramesFromNetwork", | |
179 uncached_ad_frame_bytes); | |
180 | |
181 UMA_HISTOGRAM_PERCENTAGE( | |
182 "PageLoad.Clients.Ads.Google.BytesPercent." | |
183 "AdFramesFromNetworkOfAdFramesTotal", | |
184 uncached_ad_frame_bytes * 100 / total_ad_frame_bytes); | |
185 | |
186 int percent_bytes_from_uncached_ads = | |
187 uncached_page_bytes_ == 0 | |
188 ? 0 | |
189 : uncached_ad_frame_bytes * 100 / uncached_page_bytes_; | |
190 UMA_HISTOGRAM_PERCENTAGE( | |
191 "PageLoad.Clients.Ads.Google.Bytes.Percent." | |
192 "AdFramesFromNetworkOfPageFromNetwork", | |
193 percent_bytes_from_uncached_ads); | |
194 } | |
195 | |
196 int AdsPageLoadMetricsObserver::FindAdAncestor( | |
197 content::NavigationHandle* navigation_handle) { | |
198 // We haven't seen a load from this frame before. We should have seen its | |
199 // parent though. Use the ad ancestor of its parent. | |
200 content::RenderFrameHost* parent_frame_host = | |
201 navigation_handle->GetRenderFrameHost()->GetParent(); | |
202 DCHECK(parent_frame_host); // Since this isn't a main frame. | |
203 DCHECK(base::ContainsKey(ad_ancestors_, | |
204 parent_frame_host->GetFrameTreeNodeId())); | |
205 return ad_ancestors_[parent_frame_host->GetFrameTreeNodeId()]; | |
206 } | |
207 | |
208 void AdsPageLoadMetricsObserver::ProcessDelayedResources( | |
209 FrameTreeNodeId frame_tree_node_id) { | |
210 const auto& frame_id_and_request = | |
211 delayed_resources_.find(frame_tree_node_id); | |
212 if (frame_id_and_request == delayed_resources_.end()) | |
213 return; | |
214 OnLoadedResource(frame_id_and_request->second); | |
215 } | |
OLD | NEW |