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