OLD | NEW |
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/location.h" | 7 #include "base/location.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
10 #include "base/metrics/user_metrics.h" | 10 #include "base/metrics/user_metrics.h" |
11 #include "components/page_load_metrics/browser/page_load_metrics_util.h" | 11 #include "components/page_load_metrics/browser/page_load_metrics_util.h" |
12 #include "components/page_load_metrics/common/page_load_metrics_messages.h" | 12 #include "components/page_load_metrics/common/page_load_metrics_messages.h" |
13 #include "components/page_load_metrics/common/page_load_timing.h" | 13 #include "components/page_load_metrics/common/page_load_timing.h" |
14 #include "components/rappor/rappor_service.h" | 14 #include "components/rappor/rappor_service.h" |
15 #include "components/rappor/rappor_utils.h" | 15 #include "components/rappor/rappor_utils.h" |
16 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
17 #include "content/public/browser/navigation_details.h" | 17 #include "content/public/browser/navigation_details.h" |
18 #include "content/public/browser/navigation_handle.h" | 18 #include "content/public/browser/navigation_handle.h" |
19 #include "content/public/browser/render_frame_host.h" | 19 #include "content/public/browser/render_frame_host.h" |
20 #include "content/public/browser/web_contents.h" | 20 #include "content/public/browser/web_contents.h" |
21 #include "content/public/browser/web_contents_observer.h" | 21 #include "content/public/browser/web_contents_observer.h" |
22 #include "content/public/browser/web_contents_user_data.h" | 22 #include "content/public/browser/web_contents_user_data.h" |
23 #include "ipc/ipc_message.h" | 23 #include "ipc/ipc_message.h" |
24 #include "ipc/ipc_message_macros.h" | 24 #include "ipc/ipc_message_macros.h" |
| 25 #include "ui/base/page_transition_types.h" |
25 | 26 |
26 DEFINE_WEB_CONTENTS_USER_DATA_KEY( | 27 DEFINE_WEB_CONTENTS_USER_DATA_KEY( |
27 page_load_metrics::MetricsWebContentsObserver); | 28 page_load_metrics::MetricsWebContentsObserver); |
28 | 29 |
29 namespace page_load_metrics { | 30 namespace page_load_metrics { |
30 | 31 |
31 namespace { | 32 namespace { |
32 | 33 |
33 // The url we see from the renderer side is not always the same as what | 34 // The url we see from the renderer side is not always the same as what |
34 // we see from the browser side (e.g. chrome://newtab). We want to be | 35 // we see from the browser side (e.g. chrome://newtab). We want to be |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 return false; | 75 return false; |
75 } | 76 } |
76 | 77 |
77 return true; | 78 return true; |
78 } | 79 } |
79 | 80 |
80 void RecordInternalError(InternalErrorLoadEvent event) { | 81 void RecordInternalError(InternalErrorLoadEvent event) { |
81 UMA_HISTOGRAM_ENUMERATION(kErrorEvents, event, ERR_LAST_ENTRY); | 82 UMA_HISTOGRAM_ENUMERATION(kErrorEvents, event, ERR_LAST_ENTRY); |
82 } | 83 } |
83 | 84 |
| 85 UserAbortType AbortTypeForPageTransition(ui::PageTransition transition) { |
| 86 if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD)) |
| 87 return ABORT_RELOAD; |
| 88 if (transition & ui::PAGE_TRANSITION_FORWARD_BACK) |
| 89 return ABORT_FORWARD_BACK; |
| 90 if (ui::PageTransitionIsNewNavigation(transition)) |
| 91 return ABORT_NEW_NAVIGATION; |
| 92 NOTREACHED(); |
| 93 return ABORT_OTHER; |
| 94 } |
| 95 |
84 // The number of buckets in the bitfield histogram. These buckets are described | 96 // The number of buckets in the bitfield histogram. These buckets are described |
85 // in rappor.xml in PageLoad.CoarseTiming.NavigationToFirstContentfulPaint. | 97 // in rappor.xml in PageLoad.CoarseTiming.NavigationToFirstContentfulPaint. |
86 // The bucket flag is defined by 1 << bucket_index, and is the bitfield | 98 // The bucket flag is defined by 1 << bucket_index, and is the bitfield |
87 // representing which timing bucket the page load falls into, i.e. 000010 | 99 // representing which timing bucket the page load falls into, i.e. 000010 |
88 // would be the bucket flag showing that the page took between 2 and 4 seconds | 100 // would be the bucket flag showing that the page took between 2 and 4 seconds |
89 // to load. | 101 // to load. |
90 const size_t kNumRapporHistogramBuckets = 6; | 102 const size_t kNumRapporHistogramBuckets = 6; |
91 | 103 |
92 uint64_t RapporHistogramBucketIndex(const base::TimeDelta& time) { | 104 uint64_t RapporHistogramBucketIndex(const base::TimeDelta& time) { |
93 int64 seconds = time.InSeconds(); | 105 int64 seconds = time.InSeconds(); |
(...skipping 12 matching lines...) Expand all Loading... |
106 | 118 |
107 } // namespace | 119 } // namespace |
108 | 120 |
109 PageLoadTracker::PageLoadTracker( | 121 PageLoadTracker::PageLoadTracker( |
110 bool in_foreground, | 122 bool in_foreground, |
111 PageLoadMetricsEmbedderInterface* embedder_interface, | 123 PageLoadMetricsEmbedderInterface* embedder_interface, |
112 content::NavigationHandle* navigation_handle) | 124 content::NavigationHandle* navigation_handle) |
113 : renderer_tracked_(false), | 125 : renderer_tracked_(false), |
114 has_commit_(false), | 126 has_commit_(false), |
115 navigation_start_(navigation_handle->NavigationStart()), | 127 navigation_start_(navigation_handle->NavigationStart()), |
| 128 abort_type_(ABORT_NONE), |
116 started_in_foreground_(in_foreground), | 129 started_in_foreground_(in_foreground), |
117 embedder_interface_(embedder_interface) { | 130 embedder_interface_(embedder_interface) { |
118 embedder_interface_->RegisterObservers(this); | 131 embedder_interface_->RegisterObservers(this); |
119 for (const auto& observer : observers_) { | 132 for (const auto& observer : observers_) { |
120 observer->OnStart(navigation_handle); | 133 observer->OnStart(navigation_handle); |
121 } | 134 } |
122 } | 135 } |
123 | 136 |
124 PageLoadTracker::~PageLoadTracker() { | 137 PageLoadTracker::~PageLoadTracker() { |
125 if (has_commit_) { | |
126 RecordTimingHistograms(); | |
127 RecordRappor(); | |
128 } | |
129 PageLoadExtraInfo info = GetPageLoadMetricsInfo(); | 138 PageLoadExtraInfo info = GetPageLoadMetricsInfo(); |
| 139 RecordRappor(info); |
| 140 RecordTimingHistograms(info); |
130 for (const auto& observer : observers_) { | 141 for (const auto& observer : observers_) { |
131 observer->OnComplete(timing_, info); | 142 observer->OnComplete(timing_, info); |
132 } | 143 } |
133 } | 144 } |
134 | 145 |
135 void PageLoadTracker::WebContentsHidden() { | 146 void PageLoadTracker::WebContentsHidden() { |
136 // Only log the first time we background in a given page load. | 147 // Only log the first time we background in a given page load. |
137 if (started_in_foreground_ && background_time_.is_null()) | 148 if (started_in_foreground_ && background_time_.is_null()) |
138 background_time_ = base::TimeTicks::Now(); | 149 background_time_ = base::TimeTicks::Now(); |
139 } | 150 } |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
189 } | 200 } |
190 | 201 |
191 void PageLoadTracker::AddObserver( | 202 void PageLoadTracker::AddObserver( |
192 scoped_ptr<PageLoadMetricsObserver> observer) { | 203 scoped_ptr<PageLoadMetricsObserver> observer) { |
193 observers_.push_back(std::move(observer)); | 204 observers_.push_back(std::move(observer)); |
194 } | 205 } |
195 | 206 |
196 PageLoadExtraInfo PageLoadTracker::GetPageLoadMetricsInfo() { | 207 PageLoadExtraInfo PageLoadTracker::GetPageLoadMetricsInfo() { |
197 base::TimeDelta first_background_time; | 208 base::TimeDelta first_background_time; |
198 base::TimeDelta first_foreground_time; | 209 base::TimeDelta first_foreground_time; |
| 210 base::TimeDelta time_to_abort; |
199 if (!background_time_.is_null() && started_in_foreground_) | 211 if (!background_time_.is_null() && started_in_foreground_) |
200 first_background_time = background_time_ - navigation_start_; | 212 first_background_time = background_time_ - navigation_start_; |
201 if (!foreground_time_.is_null() && !started_in_foreground_) | 213 if (!foreground_time_.is_null() && !started_in_foreground_) |
202 first_foreground_time = foreground_time_ - navigation_start_; | 214 first_foreground_time = foreground_time_ - navigation_start_; |
| 215 if (abort_type_ != ABORT_NONE) { |
| 216 DCHECK(!abort_time_.is_null()); |
| 217 time_to_abort = abort_time_ - navigation_start_; |
| 218 } |
203 return PageLoadExtraInfo(first_background_time, first_foreground_time, | 219 return PageLoadExtraInfo(first_background_time, first_foreground_time, |
204 started_in_foreground_, has_commit_); | 220 started_in_foreground_, has_commit_, abort_type_, |
| 221 time_to_abort); |
205 } | 222 } |
206 | 223 |
207 const GURL& PageLoadTracker::committed_url() { | 224 const GURL& PageLoadTracker::committed_url() { |
208 DCHECK(has_commit_); | 225 DCHECK(has_commit_); |
209 return url_; | 226 return url_; |
210 } | 227 } |
211 | 228 |
212 // Blink calculates navigation start using TimeTicks, but converts to epoch time | 229 void PageLoadTracker::NotifyAbort(UserAbortType abort_type, |
213 // in its public API. Thus, to compare time values to navigation start, we | 230 base::TimeTicks timestamp) { |
214 // calculate the current time since the epoch using TimeTicks, and convert to | 231 DCHECK_NE(abort_type, ABORT_NONE); |
215 // Time. This method is similar to how blink converts TimeTicks to epoch time. | 232 // Use UpdateAbort to update an already notified PageLoadTracker. |
216 // There may be slight inaccuracies due to inter-process timestamps, but | 233 if (abort_type_ != ABORT_NONE) |
217 // this solution is the best we have right now. | 234 return; |
218 // | 235 |
219 // returns a TimeDelta which is | 236 abort_type_ = abort_type; |
220 // - Infinity if we were never backgrounded | 237 abort_time_ = timestamp; |
221 // - null (TimeDelta()) if we started backgrounded | |
222 // - elapsed time to first background if we started in the foreground and | |
223 // backgrounded. | |
224 base::TimeDelta PageLoadTracker::GetBackgroundDelta() { | |
225 if (started_in_foreground_) { | |
226 return background_time_.is_null() ? base::TimeDelta::Max() | |
227 : background_time_ - navigation_start_; | |
228 } | |
229 return base::TimeDelta(); | |
230 } | 238 } |
231 | 239 |
232 void PageLoadTracker::RecordTimingHistograms() { | 240 void PageLoadTracker::UpdateAbort(UserAbortType abort_type, |
233 DCHECK(has_commit_); | 241 base::TimeTicks timestamp) { |
234 if (!renderer_tracked()) | 242 DCHECK_NE(abort_type, ABORT_NONE); |
| 243 DCHECK_NE(abort_type, ABORT_OTHER); |
| 244 DCHECK_EQ(abort_type_, ABORT_OTHER); |
| 245 |
| 246 abort_type_ = abort_type; |
| 247 // For some aborts (e.g. navigations), the initiated timestamp can be earlier |
| 248 // than the timestamp that aborted the load. Taking the minimum gives the |
| 249 // closest user initiated time known. |
| 250 abort_time_ = std::min(abort_time_, timestamp); |
| 251 } |
| 252 |
| 253 void PageLoadTracker::RecordTimingHistograms(const PageLoadExtraInfo& info) { |
| 254 if (!info.first_background_time.is_zero() && |
| 255 !EventOccurredInForeground(timing_.first_paint, info)) { |
| 256 if (has_commit_) { |
| 257 PAGE_LOAD_HISTOGRAM(kHistogramBackgroundBeforePaint, |
| 258 info.first_background_time); |
| 259 } else { |
| 260 PAGE_LOAD_HISTOGRAM(kHistogramBackgroundBeforeCommit, |
| 261 info.first_background_time); |
| 262 } |
| 263 } |
| 264 |
| 265 // The rest of the histograms require the load to have commit and be relevant. |
| 266 if (!has_commit_ || !renderer_tracked()) |
235 return; | 267 return; |
236 | 268 |
| 269 // The rest of the timing histograms require us to have received IPCs from the |
| 270 // renderer. Record UMA for how often this occurs (usually for quickly aborted |
| 271 // loads). For now, don't update observers if this is the case. |
237 if (timing_.IsEmpty()) { | 272 if (timing_.IsEmpty()) { |
238 RecordInternalError(ERR_NO_IPCS_RECEIVED); | 273 RecordInternalError(ERR_NO_IPCS_RECEIVED); |
239 return; | 274 return; |
240 } | 275 } |
241 | 276 |
242 base::TimeDelta background_delta = GetBackgroundDelta(); | |
243 | |
244 if (!timing_.dom_content_loaded_event_start.is_zero()) { | 277 if (!timing_.dom_content_loaded_event_start.is_zero()) { |
245 if (timing_.dom_content_loaded_event_start < background_delta) { | 278 if (EventOccurredInForeground(timing_.dom_content_loaded_event_start, |
| 279 info)) { |
246 PAGE_LOAD_HISTOGRAM(kHistogramDomContentLoaded, | 280 PAGE_LOAD_HISTOGRAM(kHistogramDomContentLoaded, |
247 timing_.dom_content_loaded_event_start); | 281 timing_.dom_content_loaded_event_start); |
248 } else { | 282 } else { |
249 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramDomContentLoaded, | 283 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramDomContentLoaded, |
250 timing_.dom_content_loaded_event_start); | 284 timing_.dom_content_loaded_event_start); |
251 } | 285 } |
252 } | 286 } |
253 if (!timing_.load_event_start.is_zero()) { | 287 if (!timing_.load_event_start.is_zero()) { |
254 if (timing_.load_event_start < background_delta) { | 288 if (EventOccurredInForeground(timing_.load_event_start, info)) { |
255 PAGE_LOAD_HISTOGRAM(kHistogramLoad, timing_.load_event_start); | 289 PAGE_LOAD_HISTOGRAM(kHistogramLoad, timing_.load_event_start); |
256 } else { | 290 } else { |
257 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramLoad, timing_.load_event_start); | 291 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramLoad, timing_.load_event_start); |
258 } | 292 } |
259 } | 293 } |
260 if (timing_.first_layout.is_zero()) { | 294 if (timing_.first_layout.is_zero()) { |
261 RecordCommittedEvent(RELEVANT_LOAD_FAILED_BEFORE_FIRST_LAYOUT, | 295 RecordCommittedEvent(RELEVANT_LOAD_FAILED_BEFORE_FIRST_LAYOUT, |
262 HasBackgrounded()); | 296 HasBackgrounded()); |
263 } else { | 297 } else { |
264 if (timing_.first_layout < background_delta) { | 298 if (EventOccurredInForeground(timing_.first_layout, info)) { |
265 PAGE_LOAD_HISTOGRAM(kHistogramFirstLayout, timing_.first_layout); | 299 PAGE_LOAD_HISTOGRAM(kHistogramFirstLayout, timing_.first_layout); |
266 RecordCommittedEvent(RELEVANT_LOAD_SUCCESSFUL_FIRST_LAYOUT, false); | 300 RecordCommittedEvent(RELEVANT_LOAD_SUCCESSFUL_FIRST_LAYOUT, false); |
267 } else { | 301 } else { |
268 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstLayout, | 302 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstLayout, |
269 timing_.first_layout); | 303 timing_.first_layout); |
270 RecordCommittedEvent(RELEVANT_LOAD_SUCCESSFUL_FIRST_LAYOUT, true); | 304 RecordCommittedEvent(RELEVANT_LOAD_SUCCESSFUL_FIRST_LAYOUT, true); |
271 } | 305 } |
272 } | 306 } |
273 if (!timing_.first_paint.is_zero()) { | 307 if (!timing_.first_paint.is_zero()) { |
274 if (timing_.first_paint < background_delta) { | 308 if (EventOccurredInForeground(timing_.first_paint, info)) { |
275 PAGE_LOAD_HISTOGRAM(kHistogramFirstPaint, timing_.first_paint); | 309 PAGE_LOAD_HISTOGRAM(kHistogramFirstPaint, timing_.first_paint); |
276 } else { | 310 } else { |
277 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstPaint, timing_.first_paint); | 311 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstPaint, timing_.first_paint); |
278 } | 312 } |
279 } | 313 } |
280 if (!timing_.first_text_paint.is_zero()) { | 314 if (!timing_.first_text_paint.is_zero()) { |
281 if (timing_.first_text_paint < background_delta) { | 315 if (EventOccurredInForeground(timing_.first_text_paint, info)) { |
282 PAGE_LOAD_HISTOGRAM(kHistogramFirstTextPaint, timing_.first_text_paint); | 316 PAGE_LOAD_HISTOGRAM(kHistogramFirstTextPaint, timing_.first_text_paint); |
283 } else { | 317 } else { |
284 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstTextPaint, | 318 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstTextPaint, |
285 timing_.first_text_paint); | 319 timing_.first_text_paint); |
286 } | 320 } |
287 } | 321 } |
288 if (!timing_.first_image_paint.is_zero()) { | 322 if (!timing_.first_image_paint.is_zero()) { |
289 if (timing_.first_image_paint < background_delta) { | 323 if (EventOccurredInForeground(timing_.first_image_paint, info)) { |
290 PAGE_LOAD_HISTOGRAM(kHistogramFirstImagePaint, timing_.first_image_paint); | 324 PAGE_LOAD_HISTOGRAM(kHistogramFirstImagePaint, timing_.first_image_paint); |
291 } else { | 325 } else { |
292 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstImagePaint, | 326 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstImagePaint, |
293 timing_.first_image_paint); | 327 timing_.first_image_paint); |
294 } | 328 } |
295 } | 329 } |
296 base::TimeDelta first_contentful_paint = GetFirstContentfulPaint(timing_); | 330 base::TimeDelta first_contentful_paint = GetFirstContentfulPaint(timing_); |
297 if (!first_contentful_paint.is_zero()) { | 331 if (!first_contentful_paint.is_zero()) { |
298 if (first_contentful_paint < background_delta) { | 332 if (EventOccurredInForeground(first_contentful_paint, info)) { |
299 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaint, | 333 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaint, |
300 first_contentful_paint); | 334 first_contentful_paint); |
301 // Bucket these histograms into high/low resolution clock systems. This | 335 // Bucket these histograms into high/low resolution clock systems. This |
302 // might point us to directions that will de-noise some UMA. | 336 // might point us to directions that will de-noise some UMA. |
303 if (base::TimeTicks::IsHighResolution()) { | 337 if (base::TimeTicks::IsHighResolution()) { |
304 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaintHigh, | 338 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaintHigh, |
305 first_contentful_paint); | 339 first_contentful_paint); |
306 } else { | 340 } else { |
307 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaintLow, | 341 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaintLow, |
308 first_contentful_paint); | 342 first_contentful_paint); |
309 } | 343 } |
310 } else { | 344 } else { |
311 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstContentfulPaint, | 345 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstContentfulPaint, |
312 first_contentful_paint); | 346 first_contentful_paint); |
313 } | 347 } |
314 } | 348 } |
315 | 349 |
316 // Log time to first foreground / time to first background. Log counts that we | 350 // Log time to first foreground / time to first background. Log counts that we |
317 // started a relevant page load in the foreground / background. | 351 // started a relevant page load in the foreground / background. |
318 if (!background_time_.is_null()) { | 352 if (!info.first_background_time.is_zero()) |
319 PAGE_LOAD_HISTOGRAM(kHistogramFirstBackground, background_delta); | 353 PAGE_LOAD_HISTOGRAM(kHistogramFirstBackground, info.first_background_time); |
320 } else if (!foreground_time_.is_null()) { | 354 else if (!info.first_foreground_time.is_zero()) |
321 PAGE_LOAD_HISTOGRAM(kHistogramFirstForeground, | 355 PAGE_LOAD_HISTOGRAM(kHistogramFirstForeground, info.first_foreground_time); |
322 foreground_time_ - navigation_start_); | |
323 } | |
324 } | 356 } |
325 | 357 |
326 void PageLoadTracker::RecordProvisionalEvent(ProvisionalLoadEvent event) { | 358 void PageLoadTracker::RecordProvisionalEvent(ProvisionalLoadEvent event) { |
327 if (HasBackgrounded()) { | 359 if (HasBackgrounded()) { |
328 UMA_HISTOGRAM_ENUMERATION(kBackgroundProvisionalEvents, event, | 360 UMA_HISTOGRAM_ENUMERATION(kBackgroundProvisionalEvents, event, |
329 PROVISIONAL_LOAD_LAST_ENTRY); | 361 PROVISIONAL_LOAD_LAST_ENTRY); |
330 } else { | 362 } else { |
331 UMA_HISTOGRAM_ENUMERATION(kProvisionalEvents, event, | 363 UMA_HISTOGRAM_ENUMERATION(kProvisionalEvents, event, |
332 PROVISIONAL_LOAD_LAST_ENTRY); | 364 PROVISIONAL_LOAD_LAST_ENTRY); |
333 } | 365 } |
334 } | 366 } |
335 | 367 |
336 // RecordCommittedEvent needs a backgrounded input because we need to special | 368 // RecordCommittedEvent needs a backgrounded input because we need to special |
337 // case a few events that need either precise timing measurements, or different | 369 // case a few events that need either precise timing measurements, or different |
338 // logic than simply "Did I background before logging this event?" | 370 // logic than simply "Did I background before logging this event?" |
339 void PageLoadTracker::RecordCommittedEvent(CommittedRelevantLoadEvent event, | 371 void PageLoadTracker::RecordCommittedEvent(CommittedRelevantLoadEvent event, |
340 bool backgrounded) { | 372 bool backgrounded) { |
341 if (backgrounded) { | 373 if (backgrounded) { |
342 UMA_HISTOGRAM_ENUMERATION(kBackgroundCommittedEvents, event, | 374 UMA_HISTOGRAM_ENUMERATION(kBackgroundCommittedEvents, event, |
343 RELEVANT_LOAD_LAST_ENTRY); | 375 RELEVANT_LOAD_LAST_ENTRY); |
344 } else { | 376 } else { |
345 UMA_HISTOGRAM_ENUMERATION(kCommittedEvents, event, | 377 UMA_HISTOGRAM_ENUMERATION(kCommittedEvents, event, |
346 RELEVANT_LOAD_LAST_ENTRY); | 378 RELEVANT_LOAD_LAST_ENTRY); |
347 } | 379 } |
348 } | 380 } |
349 | 381 |
350 void PageLoadTracker::RecordRappor() { | 382 void PageLoadTracker::RecordRappor(const PageLoadExtraInfo& info) { |
| 383 if (!info.has_commit) |
| 384 return; |
351 DCHECK(!committed_url().is_empty()); | 385 DCHECK(!committed_url().is_empty()); |
352 rappor::RapporService* rappor_service = | 386 rappor::RapporService* rappor_service = |
353 embedder_interface_->GetRapporService(); | 387 embedder_interface_->GetRapporService(); |
354 if (!rappor_service) | 388 if (!rappor_service) |
355 return; | 389 return; |
356 base::TimeDelta first_contentful_paint = GetFirstContentfulPaint(timing_); | 390 base::TimeDelta first_contentful_paint = GetFirstContentfulPaint(timing_); |
357 // Log the eTLD+1 of sites that show poor loading performance. | 391 // Log the eTLD+1 of sites that show poor loading performance. |
358 if (!first_contentful_paint.is_zero() && | 392 if (EventOccurredInForeground(first_contentful_paint, info)) { |
359 first_contentful_paint < GetBackgroundDelta()) { | |
360 scoped_ptr<rappor::Sample> sample = | 393 scoped_ptr<rappor::Sample> sample = |
361 rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE); | 394 rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE); |
362 sample->SetStringField( | 395 sample->SetStringField( |
363 "Domain", rappor::GetDomainAndRegistrySampleFromGURL(committed_url())); | 396 "Domain", rappor::GetDomainAndRegistrySampleFromGURL(committed_url())); |
364 uint64_t bucket_index = RapporHistogramBucketIndex(first_contentful_paint); | 397 uint64_t bucket_index = RapporHistogramBucketIndex(first_contentful_paint); |
365 sample->SetFlagsField("Bucket", uint64_t(1) << bucket_index, | 398 sample->SetFlagsField("Bucket", uint64_t(1) << bucket_index, |
366 kNumRapporHistogramBuckets); | 399 kNumRapporHistogramBuckets); |
367 // The IsSlow flag is just a one bit boolean if the first layout was > 10s. | 400 // The IsSlow flag is just a one bit boolean if the first layout was > 10s. |
368 sample->SetFlagsField("IsSlow", first_contentful_paint.InSecondsF() >= 10, | 401 sample->SetFlagsField("IsSlow", first_contentful_paint.InSecondsF() >= 10, |
369 1); | 402 1); |
(...skipping 17 matching lines...) Expand all Loading... |
387 | 420 |
388 MetricsWebContentsObserver* metrics = FromWebContents(web_contents); | 421 MetricsWebContentsObserver* metrics = FromWebContents(web_contents); |
389 if (!metrics) { | 422 if (!metrics) { |
390 metrics = | 423 metrics = |
391 new MetricsWebContentsObserver(web_contents, embedder_interface.Pass()); | 424 new MetricsWebContentsObserver(web_contents, embedder_interface.Pass()); |
392 web_contents->SetUserData(UserDataKey(), metrics); | 425 web_contents->SetUserData(UserDataKey(), metrics); |
393 } | 426 } |
394 return metrics; | 427 return metrics; |
395 } | 428 } |
396 | 429 |
397 MetricsWebContentsObserver::~MetricsWebContentsObserver() {} | 430 MetricsWebContentsObserver::~MetricsWebContentsObserver() { |
| 431 NotifyAbortAllLoads(ABORT_CLOSE); |
| 432 } |
398 | 433 |
399 bool MetricsWebContentsObserver::OnMessageReceived( | 434 bool MetricsWebContentsObserver::OnMessageReceived( |
400 const IPC::Message& message, | 435 const IPC::Message& message, |
401 content::RenderFrameHost* render_frame_host) { | 436 content::RenderFrameHost* render_frame_host) { |
402 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 437 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
403 bool handled = true; | 438 bool handled = true; |
404 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MetricsWebContentsObserver, message, | 439 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MetricsWebContentsObserver, message, |
405 render_frame_host) | 440 render_frame_host) |
406 IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_TimingUpdated, OnTimingUpdated) | 441 IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_TimingUpdated, OnTimingUpdated) |
407 IPC_MESSAGE_UNHANDLED(handled = false) | 442 IPC_MESSAGE_UNHANDLED(handled = false) |
(...skipping 30 matching lines...) Expand all Loading... |
438 provisional_loads_.erase(navigation_handle); | 473 provisional_loads_.erase(navigation_handle); |
439 | 474 |
440 // There's a chance a navigation could have started before we were added to a | 475 // There's a chance a navigation could have started before we were added to a |
441 // tab. Bail out early if this is the case. | 476 // tab. Bail out early if this is the case. |
442 if (!finished_nav) | 477 if (!finished_nav) |
443 return; | 478 return; |
444 | 479 |
445 // Handle a pre-commit error here. Navigations that result in an error page | 480 // Handle a pre-commit error here. Navigations that result in an error page |
446 // will be ignored. Note that downloads/204s will result in HasCommitted() | 481 // will be ignored. Note that downloads/204s will result in HasCommitted() |
447 // returning false. | 482 // returning false. |
| 483 // TODO(csharrison): Track changes to NavigationHandle for signals when this |
| 484 // is the case (HTTP response headers). |
448 if (!navigation_handle->HasCommitted()) { | 485 if (!navigation_handle->HasCommitted()) { |
449 net::Error error = navigation_handle->GetNetErrorCode(); | 486 net::Error error = navigation_handle->GetNetErrorCode(); |
450 finished_nav->RecordProvisionalEvent( | 487 ProvisionalLoadEvent event = error == net::OK ? PROVISIONAL_LOAD_STOPPED |
451 error == net::OK ? PROVISIONAL_LOAD_STOPPED | |
452 : error == net::ERR_ABORTED ? PROVISIONAL_LOAD_ERR_ABORTED | 488 : error == net::ERR_ABORTED ? PROVISIONAL_LOAD_ERR_ABORTED |
453 : PROVISIONAL_LOAD_ERR_FAILED_NON_ABORT); | 489 : PROVISIONAL_LOAD_ERR_FAILED_NON_ABORT; |
| 490 finished_nav->RecordProvisionalEvent(event); |
| 491 if (event != PROVISIONAL_LOAD_ERR_FAILED_NON_ABORT) { |
| 492 finished_nav->NotifyAbort(ABORT_OTHER, base::TimeTicks::Now()); |
| 493 aborted_provisional_loads_.push_back(std::move(finished_nav)); |
| 494 } |
454 return; | 495 return; |
455 } | 496 } |
456 finished_nav->RecordProvisionalEvent(PROVISIONAL_LOAD_COMMITTED); | 497 finished_nav->RecordProvisionalEvent(PROVISIONAL_LOAD_COMMITTED); |
457 | 498 |
458 // Don't treat a same-page nav as a new page load. | 499 // Don't treat a same-page nav as a new page load. |
459 if (navigation_handle->IsSamePage()) | 500 if (navigation_handle->IsSamePage()) |
460 return; | 501 return; |
461 | 502 |
| 503 // Notify other loads that they may have been aborted by this committed load. |
| 504 // Note that by using the committed navigation start as the abort cause, we |
| 505 // lose data on provisional loads that were aborted by other provisional |
| 506 // loads. Those will either be listed as ABORT_OTHER or as being aborted by |
| 507 // this load. |
| 508 NotifyAbortAllLoadsWithTimestamp( |
| 509 AbortTypeForPageTransition(navigation_handle->GetPageTransition()), |
| 510 navigation_handle->NavigationStart()); |
| 511 |
462 committed_load_ = finished_nav.Pass(); | 512 committed_load_ = finished_nav.Pass(); |
| 513 aborted_provisional_loads_.clear(); |
463 | 514 |
464 const GURL& browser_url = web_contents()->GetLastCommittedURL(); | 515 const GURL& browser_url = web_contents()->GetLastCommittedURL(); |
465 const std::string& mime_type = web_contents()->GetContentsMimeType(); | 516 const std::string& mime_type = web_contents()->GetContentsMimeType(); |
466 DCHECK(!browser_url.is_empty()); | 517 DCHECK(!browser_url.is_empty()); |
467 DCHECK(!mime_type.empty()); | 518 DCHECK(!mime_type.empty()); |
468 committed_load_->set_renderer_tracked( | 519 committed_load_->set_renderer_tracked( |
469 IsRelevantNavigation(navigation_handle, browser_url, mime_type)); | 520 IsRelevantNavigation(navigation_handle, browser_url, mime_type)); |
470 | 521 |
471 committed_load_->Commit(navigation_handle); | 522 committed_load_->Commit(navigation_handle); |
472 } | 523 } |
473 | 524 |
| 525 void MetricsWebContentsObserver::NavigationStopped() { |
| 526 NotifyAbortAllLoads(ABORT_STOP); |
| 527 } |
| 528 |
474 void MetricsWebContentsObserver::DidRedirectNavigation( | 529 void MetricsWebContentsObserver::DidRedirectNavigation( |
475 content::NavigationHandle* navigation_handle) { | 530 content::NavigationHandle* navigation_handle) { |
476 if (!navigation_handle->IsInMainFrame()) | 531 if (!navigation_handle->IsInMainFrame()) |
477 return; | 532 return; |
478 auto it = provisional_loads_.find(navigation_handle); | 533 auto it = provisional_loads_.find(navigation_handle); |
479 if (it == provisional_loads_.end()) | 534 if (it == provisional_loads_.end()) |
480 return; | 535 return; |
481 it->second->Redirect(navigation_handle); | 536 it->second->Redirect(navigation_handle); |
482 } | 537 } |
483 | 538 |
(...skipping 10 matching lines...) Expand all Loading... |
494 in_foreground_ = false; | 549 in_foreground_ = false; |
495 if (committed_load_) | 550 if (committed_load_) |
496 committed_load_->WebContentsHidden(); | 551 committed_load_->WebContentsHidden(); |
497 for (const auto& kv : provisional_loads_) { | 552 for (const auto& kv : provisional_loads_) { |
498 kv.second->WebContentsHidden(); | 553 kv.second->WebContentsHidden(); |
499 } | 554 } |
500 } | 555 } |
501 | 556 |
502 // This will occur when the process for the main RenderFrameHost exits, either | 557 // This will occur when the process for the main RenderFrameHost exits, either |
503 // normally or from a crash. We eagerly log data from the last committed load if | 558 // normally or from a crash. We eagerly log data from the last committed load if |
504 // we have one. | 559 // we have one. Don't notify aborts here because this is probably not user |
| 560 // initiated. If it is (e.g. browser shutdown), other code paths will take care |
| 561 // of notifying. |
505 void MetricsWebContentsObserver::RenderProcessGone( | 562 void MetricsWebContentsObserver::RenderProcessGone( |
506 base::TerminationStatus status) { | 563 base::TerminationStatus status) { |
| 564 // Other code paths will be run for normal renderer shutdown. Note that we |
| 565 // sometimes get the STILL_RUNNING value on fast shutdown. |
| 566 if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION || |
| 567 status == base::TERMINATION_STATUS_STILL_RUNNING) { |
| 568 return; |
| 569 } |
| 570 |
| 571 // If this is a crash, eagerly log the aborted provisional loads and the |
| 572 // committed load. |provisional_loads_| don't need to be destroyed here |
| 573 // because their lifetime is tied to the NavigationHandle. |
507 committed_load_.reset(); | 574 committed_load_.reset(); |
| 575 aborted_provisional_loads_.clear(); |
| 576 } |
| 577 |
| 578 void MetricsWebContentsObserver::NotifyAbortAllLoads(UserAbortType abort_type) { |
| 579 NotifyAbortAllLoadsWithTimestamp(abort_type, base::TimeTicks::Now()); |
| 580 } |
| 581 |
| 582 void MetricsWebContentsObserver::NotifyAbortAllLoadsWithTimestamp( |
| 583 UserAbortType abort_type, |
| 584 base::TimeTicks timestamp) { |
| 585 if (committed_load_) |
| 586 committed_load_->NotifyAbort(abort_type, timestamp); |
| 587 for (const auto& kv : provisional_loads_) { |
| 588 kv.second->NotifyAbort(abort_type, timestamp); |
| 589 } |
| 590 // If a better signal than ABORT_OTHER came in the last 100ms, treat it as the |
| 591 // cause of the abort (some ABORT_OTHER signals occur before the true signal). |
| 592 // Note that |abort_type| can only be ABORT_OTHER for provisional loads. While |
| 593 // this heuristic is coarse, it works better and is simpler than other |
| 594 // feasible methods. See https://goo.gl/WKRG98. |
| 595 for (const auto& tracker : aborted_provisional_loads_) { |
| 596 // Note that |timestamp - abort_time| can be negative. |
| 597 if (tracker->abort_type() == ABORT_OTHER && |
| 598 (timestamp - tracker->abort_time()).InMilliseconds() < 100) { |
| 599 tracker->UpdateAbort(abort_type, timestamp); |
| 600 } |
| 601 } |
508 } | 602 } |
509 | 603 |
510 void MetricsWebContentsObserver::OnTimingUpdated( | 604 void MetricsWebContentsObserver::OnTimingUpdated( |
511 content::RenderFrameHost* render_frame_host, | 605 content::RenderFrameHost* render_frame_host, |
512 const PageLoadTiming& timing) { | 606 const PageLoadTiming& timing) { |
513 bool error = false; | 607 bool error = false; |
514 if (!committed_load_ || !committed_load_->renderer_tracked()) { | 608 if (!committed_load_ || !committed_load_->renderer_tracked()) { |
515 RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD); | 609 RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD); |
516 error = true; | 610 error = true; |
517 } | 611 } |
(...skipping 18 matching lines...) Expand all Loading... |
536 | 630 |
537 if (!committed_load_->UpdateTiming(timing)) { | 631 if (!committed_load_->UpdateTiming(timing)) { |
538 // If the page load tracker cannot update its timing, something is wrong | 632 // If the page load tracker cannot update its timing, something is wrong |
539 // with the IPC (it's from another load, or it's invalid in some other way). | 633 // with the IPC (it's from another load, or it's invalid in some other way). |
540 // We expect this to be a rare occurrence. | 634 // We expect this to be a rare occurrence. |
541 RecordInternalError(ERR_BAD_TIMING_IPC); | 635 RecordInternalError(ERR_BAD_TIMING_IPC); |
542 } | 636 } |
543 } | 637 } |
544 | 638 |
545 } // namespace page_load_metrics | 639 } // namespace page_load_metrics |
OLD | NEW |