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

Side by Side Diff: components/page_load_metrics/browser/metrics_web_contents_observer.cc

Issue 1384213002: Page Abort Events for relevant navigations (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Comments/name changes. Don't track non html mimes Created 5 years, 2 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
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h" 5 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/metrics/histogram.h" 8 #include "base/metrics/histogram.h"
9 #include "components/page_load_metrics/common/page_load_metrics_messages.h" 9 #include "components/page_load_metrics/common/page_load_metrics_messages.h"
10 #include "components/page_load_metrics/common/page_load_timing.h" 10 #include "components/page_load_metrics/common/page_load_timing.h"
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 } 57 }
58 58
59 } // namespace 59 } // namespace
60 60
61 #define PAGE_LOAD_HISTOGRAM(name, sample) \ 61 #define PAGE_LOAD_HISTOGRAM(name, sample) \
62 UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, \ 62 UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, \
63 base::TimeDelta::FromMilliseconds(10), \ 63 base::TimeDelta::FromMilliseconds(10), \
64 base::TimeDelta::FromMinutes(10), 100) 64 base::TimeDelta::FromMinutes(10), 100)
65 65
66 PageLoadTracker::PageLoadTracker(bool in_foreground) 66 PageLoadTracker::PageLoadTracker(bool in_foreground)
67 : has_commit_(false), started_in_foreground_(in_foreground) { 67 : has_commit_(false), started_in_foreground_(in_foreground) {}
68 RecordEvent(PAGE_LOAD_STARTED);
69 }
70 68
71 PageLoadTracker::~PageLoadTracker() { 69 PageLoadTracker::~PageLoadTracker() {
72 // Even a load that failed a provisional load should log
73 // that it aborted before first layout.
74 if (timing_.first_layout.is_zero())
75 RecordEvent(PAGE_LOAD_ABORTED_BEFORE_FIRST_LAYOUT);
76
77 if (has_commit_) 70 if (has_commit_)
78 RecordTimingHistograms(); 71 RecordTimingHistograms();
79 } 72 }
80 73
81 void PageLoadTracker::WebContentsHidden() { 74 void PageLoadTracker::WebContentsHidden() {
82 // Only log the first time we background in a given page load. 75 // Only log the first time we background in a given page load.
83 if (background_time_.is_null()) { 76 if (started_in_foreground_ && background_time_.is_null())
84 background_time_ = base::TimeTicks::Now(); 77 background_time_ = base::TimeTicks::Now();
85 } 78 }
79
80 void PageLoadTracker::WebContentsShown() {
81 // Only log the first time we foreground in a given page load.
82 // Don't log foreground time if we started foregrounded.
83 if (!started_in_foreground_ && foreground_time_.is_null())
84 foreground_time_ = base::TimeTicks::Now();
86 } 85 }
87 86
88 void PageLoadTracker::Commit() { 87 void PageLoadTracker::Commit() {
89 has_commit_ = true; 88 has_commit_ = true;
89 // We log the event that this load started. Because we don't know if a load is
90 // relevant or if it will commit before now, we have to log this event at
91 // commit time.
92 RecordCommittedEvent(COMMITTED_LOAD_STARTED, !started_in_foreground_);
90 } 93 }
91 94
92 bool PageLoadTracker::UpdateTiming(const PageLoadTiming& timing) { 95 bool PageLoadTracker::UpdateTiming(const PageLoadTiming& new_timing) {
93 // Throw away IPCs that are not relevant to the current navigation. 96 // Throw away IPCs that are not relevant to the current navigation.
94 if (!timing_.navigation_start.is_null() && 97 // Two timing structures cannot refer to the same navigation if they indicate
95 timing_.navigation_start != timing.navigation_start) { 98 // that a navigation started at different times, so a new timing struct with a
96 // TODO(csharrison) uma log a counter here 99 // different start time from an earlier struct is considered invalid.
97 return false; 100 bool valid_timing_descendent =
98 } 101 timing_.navigation_start.is_null() ||
99 if (IsValidPageLoadTiming(timing)) { 102 timing_.navigation_start == new_timing.navigation_start;
100 timing_ = timing; 103 if (IsValidPageLoadTiming(new_timing) && valid_timing_descendent) {
104 timing_ = new_timing;
101 return true; 105 return true;
102 } 106 }
103 return false; 107 return false;
104 } 108 }
105 109
110 bool PageLoadTracker::HasBackgrounded() {
111 return !(started_in_foreground_ && background_time_.is_null());
Alexei Svitkine (slow) 2015/10/16 15:43:14 Nit: !(a && b) -> !a || !b
Charlie Harrison 2015/10/16 16:05:06 Done.
112 }
113
106 void PageLoadTracker::RecordTimingHistograms() { 114 void PageLoadTracker::RecordTimingHistograms() {
107 DCHECK(has_commit_); 115 DCHECK(has_commit_);
108 // This method is similar to how blink converts TimeTicks to epoch time. 116 // This method is similar to how blink converts TimeTicks to epoch time.
109 // There may be slight inaccuracies due to inter-process timestamps, but 117 // There may be slight inaccuracies due to inter-process timestamps, but
110 // this solution is the best we have right now. 118 // this solution is the best we have right now.
111 base::TimeDelta background_delta; 119 base::TimeDelta background_delta;
112 if (started_in_foreground_) { 120 if (started_in_foreground_) {
113 background_delta = background_time_.is_null() 121 background_delta = background_time_.is_null()
114 ? base::TimeDelta::Max() 122 ? base::TimeDelta::Max()
115 : WallTimeFromTimeTicks(background_time_) - timing_.navigation_start; 123 : WallTimeFromTimeTicks(background_time_) - timing_.navigation_start;
116 } 124 }
117 125
118 if (!timing_.dom_content_loaded_event_start.is_zero()) { 126 if (!timing_.dom_content_loaded_event_start.is_zero()) {
119 if (timing_.dom_content_loaded_event_start < background_delta) { 127 if (timing_.dom_content_loaded_event_start < background_delta) {
120 PAGE_LOAD_HISTOGRAM( 128 PAGE_LOAD_HISTOGRAM(
121 "PageLoad.Timing2.NavigationToDOMContentLoadedEventFired", 129 "PageLoad.Timing2.NavigationToDOMContentLoadedEventFired",
122 timing_.dom_content_loaded_event_start); 130 timing_.dom_content_loaded_event_start);
123 } else { 131 } else {
124 PAGE_LOAD_HISTOGRAM( 132 PAGE_LOAD_HISTOGRAM(
125 "PageLoad.Timing2.NavigationToDOMContentLoadedEventFired.BG", 133 "PageLoad.Timing2.NavigationToDOMContentLoadedEventFired.Background",
126 timing_.dom_content_loaded_event_start); 134 timing_.dom_content_loaded_event_start);
127 } 135 }
128 } 136 }
129 if (!timing_.load_event_start.is_zero()) { 137 if (!timing_.load_event_start.is_zero()) {
130 if (timing_.load_event_start < background_delta) { 138 if (timing_.load_event_start < background_delta) {
131 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToLoadEventFired", 139 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToLoadEventFired",
132 timing_.load_event_start); 140 timing_.load_event_start);
133 } else { 141 } else {
134 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToLoadEventFired.BG", 142 PAGE_LOAD_HISTOGRAM(
135 timing_.load_event_start); 143 "PageLoad.Timing2.NavigationToLoadEventFired.Background",
144 timing_.load_event_start);
136 } 145 }
137 } 146 }
138 if (!timing_.first_layout.is_zero()) { 147 if (timing_.first_layout.is_zero()) {
148 RecordCommittedEvent(COMMITTED_LOAD_FAILED_BEFORE_FIRST_LAYOUT,
149 HasBackgrounded());
150 } else {
139 if (timing_.first_layout < background_delta) { 151 if (timing_.first_layout < background_delta) {
140 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstLayout", 152 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstLayout",
141 timing_.first_layout); 153 timing_.first_layout);
142 RecordEvent(PAGE_LOAD_SUCCESSFUL_FIRST_LAYOUT_FOREGROUND); 154 RecordCommittedEvent(COMMITTED_LOAD_SUCCESSFUL_FIRST_LAYOUT, false);
143 } else { 155 } else {
144 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstLayout.BG", 156 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstLayout.Background",
145 timing_.first_layout); 157 timing_.first_layout);
146 RecordEvent(PAGE_LOAD_SUCCESSFUL_FIRST_LAYOUT_BACKGROUND); 158 RecordCommittedEvent(COMMITTED_LOAD_SUCCESSFUL_FIRST_LAYOUT, true);
147 } 159 }
148 } 160 }
149 if (!timing_.first_text_paint.is_zero()) { 161 if (!timing_.first_text_paint.is_zero()) {
150 if (timing_.first_text_paint < background_delta) { 162 if (timing_.first_text_paint < background_delta) {
151 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstTextPaint", 163 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstTextPaint",
152 timing_.first_text_paint); 164 timing_.first_text_paint);
153 } else { 165 } else {
154 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstTextPaint.BG", 166 PAGE_LOAD_HISTOGRAM(
155 timing_.first_text_paint); 167 "PageLoad.Timing2.NavigationToFirstTextPaint.Background",
168 timing_.first_text_paint);
156 } 169 }
157 } 170 }
171 // Log time to first foreground / time to first background. Log counts that we
172 // started a relevant page load in the foreground / background.
173 if (!background_time_.is_null()) {
174 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstBackground",
175 background_delta);
176 } else if (!foreground_time_.is_null()) {
177 PAGE_LOAD_HISTOGRAM(
178 "PageLoad.Timing2.NavigationToFirstForeground",
179 WallTimeFromTimeTicks(foreground_time_) - timing_.navigation_start);
180 }
158 } 181 }
159 182
160 void PageLoadTracker::RecordEvent(PageLoadEvent event) { 183 void PageLoadTracker::RecordProvisionalEvent(ProvisionalLoadEvent event) {
161 UMA_HISTOGRAM_ENUMERATION( 184 if (HasBackgrounded()) {
162 "PageLoad.EventCounts", event, PAGE_LOAD_LAST_ENTRY); 185 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.Provisional.Background", event,
186 PROVISIONAL_LOAD_LAST_ENTRY);
187 } else {
188 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.Provisional", event,
189 PROVISIONAL_LOAD_LAST_ENTRY);
190 }
191 }
192
193 // RecordCommittedEvent needs a backgrounded input because we need to special
194 // case a few events that need either precise timing measurements, or different
195 // logic than simply "Did I background before logging this event?"
196 void PageLoadTracker::RecordCommittedEvent(CommittedLoadEvent event,
197 bool backgrounded) {
198 if (backgrounded) {
199 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.Committed.Background", event,
200 COMMITTED_LOAD_LAST_ENTRY);
201 } else {
202 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.Committed", event,
203 COMMITTED_LOAD_LAST_ENTRY);
204 }
163 } 205 }
164 206
165 MetricsWebContentsObserver::MetricsWebContentsObserver( 207 MetricsWebContentsObserver::MetricsWebContentsObserver(
166 content::WebContents* web_contents) 208 content::WebContents* web_contents)
167 : content::WebContentsObserver(web_contents), in_foreground_(false) {} 209 : content::WebContentsObserver(web_contents), in_foreground_(false) {}
168 210
169 MetricsWebContentsObserver::~MetricsWebContentsObserver() {} 211 MetricsWebContentsObserver::~MetricsWebContentsObserver() {}
170 212
171 bool MetricsWebContentsObserver::OnMessageReceived( 213 bool MetricsWebContentsObserver::OnMessageReceived(
172 const IPC::Message& message, 214 const IPC::Message& message,
(...skipping 23 matching lines...) Expand all
196 void MetricsWebContentsObserver::DidFinishNavigation( 238 void MetricsWebContentsObserver::DidFinishNavigation(
197 content::NavigationHandle* navigation_handle) { 239 content::NavigationHandle* navigation_handle) {
198 if (!navigation_handle->IsInMainFrame()) 240 if (!navigation_handle->IsInMainFrame())
199 return; 241 return;
200 242
201 scoped_ptr<PageLoadTracker> finished_nav( 243 scoped_ptr<PageLoadTracker> finished_nav(
202 provisional_loads_.take_and_erase(navigation_handle)); 244 provisional_loads_.take_and_erase(navigation_handle));
203 DCHECK(finished_nav); 245 DCHECK(finished_nav);
204 246
205 // Handle a pre-commit error here. Navigations that result in an error page 247 // Handle a pre-commit error here. Navigations that result in an error page
206 // will be ignored. 248 // will be ignored. Note that downloads/204s will result in HasCommitted()
249 // returning false.
207 if (!navigation_handle->HasCommitted()) { 250 if (!navigation_handle->HasCommitted()) {
208 finished_nav->RecordEvent(PAGE_LOAD_FAILED_BEFORE_COMMIT); 251 net::Error error = navigation_handle->GetNetErrorCode();
209 if (navigation_handle->GetNetErrorCode() == net::ERR_ABORTED) 252 finished_nav->RecordProvisionalEvent(
210 finished_nav->RecordEvent(PAGE_LOAD_ABORTED_BEFORE_COMMIT); 253 error == net::OK ? PROVISIONAL_LOAD_STOPPED
254 : error == net::ERR_ABORTED ? PROVISIONAL_LOAD_ERR_ABORTED
255 : PROVISIONAL_LOAD_ERR_FAILED_NON_ABORT);
211 return; 256 return;
212 } 257 }
258 finished_nav->RecordProvisionalEvent(PROVISIONAL_LOAD_COMMITTED);
213 259
214 // Don't treat a same-page nav as a new page load. 260 // Don't treat a same-page nav as a new page load.
215 if (navigation_handle->IsSamePage()) 261 if (navigation_handle->IsSamePage())
216 return; 262 return;
217 263
218 // Eagerly log the previous UMA even if we don't care about the current 264 // Eagerly log the previous UMA even if we don't care about the current
219 // navigation. 265 // navigation.
220 committed_load_.reset(); 266 committed_load_.reset();
221 267
222 if (!IsRelevantNavigation(navigation_handle)) 268 if (!IsRelevantNavigation(navigation_handle))
223 return; 269 return;
224 270
225 committed_load_ = finished_nav.Pass(); 271 committed_load_ = finished_nav.Pass();
226 committed_load_->Commit(); 272 committed_load_->Commit();
227 } 273 }
228 274
229 void MetricsWebContentsObserver::WasShown() { 275 void MetricsWebContentsObserver::WasShown() {
230 in_foreground_ = true; 276 in_foreground_ = true;
277 if (committed_load_)
278 committed_load_->WebContentsShown();
279 for (const auto& kv : provisional_loads_) {
280 kv.second->WebContentsShown();
281 }
231 } 282 }
232 283
233 void MetricsWebContentsObserver::WasHidden() { 284 void MetricsWebContentsObserver::WasHidden() {
234 in_foreground_ = false; 285 in_foreground_ = false;
235 if (committed_load_) 286 if (committed_load_)
236 committed_load_->WebContentsHidden(); 287 committed_load_->WebContentsHidden();
237 for (const auto& kv : provisional_loads_) { 288 for (const auto& kv : provisional_loads_) {
238 kv.second->WebContentsHidden(); 289 kv.second->WebContentsHidden();
239 } 290 }
240 } 291 }
241 292
242 // This will occur when the process for the main RenderFrameHost exits, either 293 // This will occur when the process for the main RenderFrameHost exits, either
243 // normally or from a crash. We eagerly log data from the last committed load if 294 // normally or from a crash. We eagerly log data from the last committed load if
244 // we have one. 295 // we have one.
245 void MetricsWebContentsObserver::RenderProcessGone( 296 void MetricsWebContentsObserver::RenderProcessGone(
246 base::TerminationStatus status) { 297 base::TerminationStatus status) {
247 committed_load_.reset(); 298 committed_load_.reset();
248 } 299 }
249 300
250 void MetricsWebContentsObserver::OnTimingUpdated( 301 void MetricsWebContentsObserver::OnTimingUpdated(
251 content::RenderFrameHost* render_frame_host, 302 content::RenderFrameHost* render_frame_host,
252 const PageLoadTiming& timing) { 303 const PageLoadTiming& timing) {
253 if (!committed_load_) 304 bool error = false;
254 return; 305 if (!committed_load_) {
306 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.InternalError",
307 ERR_IPC_WITH_NO_COMMITTED_LOAD, ERR_LAST_ENTRY);
308 error = true;
309 }
255 310
256 // We may receive notifications from frames that have been navigated away 311 // We may receive notifications from frames that have been navigated away
257 // from. We simply ignore them. 312 // from. We simply ignore them.
258 if (render_frame_host != web_contents()->GetMainFrame()) 313 if (render_frame_host != web_contents()->GetMainFrame()) {
259 return; 314 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.InternalError",
315 ERR_IPC_FROM_WRONG_FRAME, ERR_LAST_ENTRY);
Alexei Svitkine (slow) 2015/10/16 15:43:15 Please make a helper function for this histogram,
Charlie Harrison 2015/10/16 16:05:06 Done. Good call.
316 error = true;
317 }
260 318
261 // For urls like chrome://newtab, the renderer and browser disagree, 319 // For urls like chrome://newtab, the renderer and browser disagree,
262 // so we have to double check that the renderer isn't sending data from a 320 // so we have to double check that the renderer isn't sending data from a
263 // bad url like https://www.google.com/_/chrome/newtab. 321 // bad url like https://www.google.com/_/chrome/newtab.
264 if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) 322 if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
323 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.InternalError",
324 ERR_IPC_FROM_BAD_URL_SCHEME, ERR_LAST_ENTRY);
325 error = true;
326 }
327
328 if (error)
265 return; 329 return;
266 330
267 committed_load_->UpdateTiming(timing); 331 if (!committed_load_->UpdateTiming(timing)) {
332 // If the page load tracker cannot update its timing, something is wrong
333 // with the IPC (it's from another load, or it's invalid in some other way).
334 // We expect this to be a rare occurrence.
335 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.InternalError",
336 ERR_BAD_TIMING_IPC, ERR_LAST_ENTRY);
337 }
268 } 338 }
269 339
270 bool MetricsWebContentsObserver::IsRelevantNavigation( 340 bool MetricsWebContentsObserver::IsRelevantNavigation(
271 content::NavigationHandle* navigation_handle) { 341 content::NavigationHandle* navigation_handle) {
272 // The url we see from the renderer side is not always the same as what 342 // The url we see from the renderer side is not always the same as what
273 // we see from the browser side (e.g. chrome://newtab). We want to be 343 // we see from the browser side (e.g. chrome://newtab). We want to be
274 // sure here that we aren't logging UMA for internal pages. 344 // sure here that we aren't logging UMA for internal pages.
275 const GURL& browser_url = web_contents()->GetLastCommittedURL(); 345 const GURL& browser_url = web_contents()->GetLastCommittedURL();
346 std::string mime = web_contents()->GetContentsMimeType();
Bryan McQuade 2015/10/16 15:01:29 GetContentsMimeType only returns the correct value
Bryan McQuade 2015/10/16 15:17:10 nit: since GetContentsMimeType returns a const std
Charlie Harrison 2015/10/16 16:05:06 Done.
276 return navigation_handle->IsInMainFrame() && 347 return navigation_handle->IsInMainFrame() &&
277 !navigation_handle->IsSamePage() && 348 !navigation_handle->IsSamePage() &&
278 !navigation_handle->IsErrorPage() && 349 !navigation_handle->IsErrorPage() &&
279 navigation_handle->GetURL().SchemeIsHTTPOrHTTPS() && 350 navigation_handle->GetURL().SchemeIsHTTPOrHTTPS() &&
280 browser_url.SchemeIsHTTPOrHTTPS(); 351 browser_url.SchemeIsHTTPOrHTTPS() &&
352 (mime == "text/html" || mime == "application/xhtml+xml");
281 } 353 }
282 354
283 } // namespace page_load_metrics 355 } // namespace page_load_metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698