| 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 "components/page_load_metrics/browser/page_load_metrics_macros.h" | 10 #include "components/page_load_metrics/browser/page_load_metrics_macros.h" |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 if (!timing.load_event_start.is_zero() && | 69 if (!timing.load_event_start.is_zero() && |
| 70 (timing.dom_content_loaded_event_start.is_zero() || | 70 (timing.dom_content_loaded_event_start.is_zero() || |
| 71 timing.response_start > timing.load_event_start || | 71 timing.response_start > timing.load_event_start || |
| 72 timing.dom_content_loaded_event_start > timing.load_event_start)) { | 72 timing.dom_content_loaded_event_start > timing.load_event_start)) { |
| 73 return false; | 73 return false; |
| 74 } | 74 } |
| 75 | 75 |
| 76 return true; | 76 return true; |
| 77 } | 77 } |
| 78 | 78 |
| 79 base::Time WallTimeFromTimeTicks(const base::TimeTicks& time) { | |
| 80 return base::Time::FromDoubleT( | |
| 81 (time - base::TimeTicks::UnixEpoch()).InSecondsF()); | |
| 82 } | |
| 83 | |
| 84 void RecordInternalError(InternalErrorLoadEvent event) { | 79 void RecordInternalError(InternalErrorLoadEvent event) { |
| 85 UMA_HISTOGRAM_ENUMERATION(kErrorEvents, event, ERR_LAST_ENTRY); | 80 UMA_HISTOGRAM_ENUMERATION(kErrorEvents, event, ERR_LAST_ENTRY); |
| 86 } | 81 } |
| 87 | 82 |
| 88 base::TimeDelta GetFirstContentfulPaint(const PageLoadTiming& timing) { | 83 base::TimeDelta GetFirstContentfulPaint(const PageLoadTiming& timing) { |
| 89 if (timing.first_text_paint.is_zero()) | 84 if (timing.first_text_paint.is_zero()) |
| 90 return timing.first_image_paint; | 85 return timing.first_image_paint; |
| 91 if (timing.first_image_paint.is_zero()) | 86 if (timing.first_image_paint.is_zero()) |
| 92 return timing.first_text_paint; | 87 return timing.first_text_paint; |
| 93 return std::min(timing.first_text_paint, timing.first_image_paint); | 88 return std::min(timing.first_text_paint, timing.first_image_paint); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 114 if (seconds < 32) | 109 if (seconds < 32) |
| 115 return 4; | 110 return 4; |
| 116 return 5; | 111 return 5; |
| 117 } | 112 } |
| 118 | 113 |
| 119 } // namespace | 114 } // namespace |
| 120 | 115 |
| 121 PageLoadTracker::PageLoadTracker( | 116 PageLoadTracker::PageLoadTracker( |
| 122 bool in_foreground, | 117 bool in_foreground, |
| 123 PageLoadMetricsEmbedderInterface* embedder_interface, | 118 PageLoadMetricsEmbedderInterface* embedder_interface, |
| 119 content::NavigationHandle* navigation_handle, |
| 124 base::ObserverList<PageLoadMetricsObserver, true>* observers) | 120 base::ObserverList<PageLoadMetricsObserver, true>* observers) |
| 125 : has_commit_(false), | 121 : has_commit_(false), |
| 122 navigation_start_(navigation_handle->NavigationStart()), |
| 126 started_in_foreground_(in_foreground), | 123 started_in_foreground_(in_foreground), |
| 127 embedder_interface_(embedder_interface), | 124 embedder_interface_(embedder_interface), |
| 128 observers_(observers) {} | 125 observers_(observers) {} |
| 129 | 126 |
| 130 PageLoadTracker::~PageLoadTracker() { | 127 PageLoadTracker::~PageLoadTracker() { |
| 131 if (has_commit_) { | 128 if (has_commit_) { |
| 132 RecordTimingHistograms(); | 129 RecordTimingHistograms(); |
| 133 RecordRappor(); | 130 RecordRappor(); |
| 134 } | 131 } |
| 135 } | 132 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 return false; | 171 return false; |
| 175 } | 172 } |
| 176 | 173 |
| 177 bool PageLoadTracker::HasBackgrounded() { | 174 bool PageLoadTracker::HasBackgrounded() { |
| 178 return !started_in_foreground_ || !background_time_.is_null(); | 175 return !started_in_foreground_ || !background_time_.is_null(); |
| 179 } | 176 } |
| 180 | 177 |
| 181 PageLoadExtraInfo PageLoadTracker::GetPageLoadMetricsInfo() { | 178 PageLoadExtraInfo PageLoadTracker::GetPageLoadMetricsInfo() { |
| 182 base::TimeDelta first_background_time; | 179 base::TimeDelta first_background_time; |
| 183 base::TimeDelta first_foreground_time; | 180 base::TimeDelta first_foreground_time; |
| 184 if (!background_time_.is_null() && started_in_foreground_) { | 181 if (!background_time_.is_null() && started_in_foreground_) |
| 185 first_background_time = | 182 first_background_time = background_time_ - navigation_start_; |
| 186 WallTimeFromTimeTicks(background_time_) - timing_.navigation_start; | 183 if (!foreground_time_.is_null() && !started_in_foreground_) |
| 187 } | 184 first_foreground_time = foreground_time_ - navigation_start_; |
| 188 if (!foreground_time_.is_null() && !started_in_foreground_) { | |
| 189 first_foreground_time = | |
| 190 WallTimeFromTimeTicks(foreground_time_) - timing_.navigation_start; | |
| 191 } | |
| 192 return PageLoadExtraInfo(first_background_time, first_foreground_time, | 185 return PageLoadExtraInfo(first_background_time, first_foreground_time, |
| 193 started_in_foreground_); | 186 started_in_foreground_); |
| 194 } | 187 } |
| 195 | 188 |
| 196 const GURL& PageLoadTracker::GetCommittedURL() { | 189 const GURL& PageLoadTracker::GetCommittedURL() { |
| 197 DCHECK(has_commit_); | 190 DCHECK(has_commit_); |
| 198 return url_; | 191 return url_; |
| 199 } | 192 } |
| 200 | 193 |
| 201 // Blink calculates navigation start using TimeTicks, but converts to epoch time | 194 // Blink calculates navigation start using TimeTicks, but converts to epoch time |
| 202 // in its public API. Thus, to compare time values to navigation start, we | 195 // in its public API. Thus, to compare time values to navigation start, we |
| 203 // calculate the current time since the epoch using TimeTicks, and convert to | 196 // calculate the current time since the epoch using TimeTicks, and convert to |
| 204 // Time. This method is similar to how blink converts TimeTicks to epoch time. | 197 // Time. This method is similar to how blink converts TimeTicks to epoch time. |
| 205 // There may be slight inaccuracies due to inter-process timestamps, but | 198 // There may be slight inaccuracies due to inter-process timestamps, but |
| 206 // this solution is the best we have right now. | 199 // this solution is the best we have right now. |
| 207 // | 200 // |
| 208 // returns a TimeDelta which is | 201 // returns a TimeDelta which is |
| 209 // - Infinity if we were never backgrounded | 202 // - Infinity if we were never backgrounded |
| 210 // - null (TimeDelta()) if we started backgrounded | 203 // - null (TimeDelta()) if we started backgrounded |
| 211 // - elapsed time to first background if we started in the foreground and | 204 // - elapsed time to first background if we started in the foreground and |
| 212 // backgrounded. | 205 // backgrounded. |
| 213 base::TimeDelta PageLoadTracker::GetBackgroundDelta() { | 206 base::TimeDelta PageLoadTracker::GetBackgroundDelta() { |
| 214 if (started_in_foreground_) { | 207 if (started_in_foreground_) { |
| 215 if (background_time_.is_null()) | 208 return background_time_.is_null() ? base::TimeDelta::Max() |
| 216 return base::TimeDelta::Max(); | 209 : background_time_ - navigation_start_; |
| 217 return WallTimeFromTimeTicks(background_time_) - timing_.navigation_start; | |
| 218 } | 210 } |
| 219 return base::TimeDelta(); | 211 return base::TimeDelta(); |
| 220 } | 212 } |
| 221 | 213 |
| 222 void PageLoadTracker::RecordTimingHistograms() { | 214 void PageLoadTracker::RecordTimingHistograms() { |
| 223 DCHECK(has_commit_); | 215 DCHECK(has_commit_); |
| 224 if (timing_.IsEmpty()) { | 216 if (timing_.IsEmpty()) { |
| 225 RecordInternalError(ERR_NO_IPCS_RECEIVED); | 217 RecordInternalError(ERR_NO_IPCS_RECEIVED); |
| 226 return; | 218 return; |
| 227 } | 219 } |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 293 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstContentfulPaint, | 285 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstContentfulPaint, |
| 294 first_contentful_paint); | 286 first_contentful_paint); |
| 295 } | 287 } |
| 296 } | 288 } |
| 297 | 289 |
| 298 // Log time to first foreground / time to first background. Log counts that we | 290 // Log time to first foreground / time to first background. Log counts that we |
| 299 // started a relevant page load in the foreground / background. | 291 // started a relevant page load in the foreground / background. |
| 300 if (!background_time_.is_null()) { | 292 if (!background_time_.is_null()) { |
| 301 PAGE_LOAD_HISTOGRAM(kHistogramFirstBackground, background_delta); | 293 PAGE_LOAD_HISTOGRAM(kHistogramFirstBackground, background_delta); |
| 302 } else if (!foreground_time_.is_null()) { | 294 } else if (!foreground_time_.is_null()) { |
| 303 PAGE_LOAD_HISTOGRAM( | 295 PAGE_LOAD_HISTOGRAM(kHistogramFirstForeground, |
| 304 kHistogramFirstForeground, | 296 foreground_time_ - navigation_start_); |
| 305 WallTimeFromTimeTicks(foreground_time_) - timing_.navigation_start); | |
| 306 } | 297 } |
| 307 } | 298 } |
| 308 | 299 |
| 309 void PageLoadTracker::RecordProvisionalEvent(ProvisionalLoadEvent event) { | 300 void PageLoadTracker::RecordProvisionalEvent(ProvisionalLoadEvent event) { |
| 310 if (HasBackgrounded()) { | 301 if (HasBackgrounded()) { |
| 311 UMA_HISTOGRAM_ENUMERATION(kBackgroundProvisionalEvents, event, | 302 UMA_HISTOGRAM_ENUMERATION(kBackgroundProvisionalEvents, event, |
| 312 PROVISIONAL_LOAD_LAST_ENTRY); | 303 PROVISIONAL_LOAD_LAST_ENTRY); |
| 313 } else { | 304 } else { |
| 314 UMA_HISTOGRAM_ENUMERATION(kProvisionalEvents, event, | 305 UMA_HISTOGRAM_ENUMERATION(kProvisionalEvents, event, |
| 315 PROVISIONAL_LOAD_LAST_ENTRY); | 306 PROVISIONAL_LOAD_LAST_ENTRY); |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 414 return; | 405 return; |
| 415 if (embedder_interface_->IsPrerendering(web_contents())) | 406 if (embedder_interface_->IsPrerendering(web_contents())) |
| 416 return; | 407 return; |
| 417 // We can have two provisional loads in some cases. E.g. a same-site | 408 // We can have two provisional loads in some cases. E.g. a same-site |
| 418 // navigation can have a concurrent cross-process navigation started | 409 // navigation can have a concurrent cross-process navigation started |
| 419 // from the omnibox. | 410 // from the omnibox. |
| 420 DCHECK_GT(2ul, provisional_loads_.size()); | 411 DCHECK_GT(2ul, provisional_loads_.size()); |
| 421 // Passing raw pointers to observers_ and embedder_interface_ is safe because | 412 // Passing raw pointers to observers_ and embedder_interface_ is safe because |
| 422 // the MetricsWebContentsObserver owns them both list and they are torn down | 413 // the MetricsWebContentsObserver owns them both list and they are torn down |
| 423 // after the PageLoadTracker. | 414 // after the PageLoadTracker. |
| 424 provisional_loads_.insert( | 415 provisional_loads_.insert(navigation_handle, |
| 425 navigation_handle, | 416 make_scoped_ptr(new PageLoadTracker( |
| 426 make_scoped_ptr(new PageLoadTracker( | 417 in_foreground_, embedder_interface_.get(), |
| 427 in_foreground_, embedder_interface_.get(), &observers_))); | 418 navigation_handle, &observers_))); |
| 428 } | 419 } |
| 429 | 420 |
| 430 void MetricsWebContentsObserver::DidFinishNavigation( | 421 void MetricsWebContentsObserver::DidFinishNavigation( |
| 431 content::NavigationHandle* navigation_handle) { | 422 content::NavigationHandle* navigation_handle) { |
| 432 if (!navigation_handle->IsInMainFrame()) | 423 if (!navigation_handle->IsInMainFrame()) |
| 433 return; | 424 return; |
| 434 | 425 |
| 435 scoped_ptr<PageLoadTracker> finished_nav( | 426 scoped_ptr<PageLoadTracker> finished_nav( |
| 436 provisional_loads_.take_and_erase(navigation_handle)); | 427 provisional_loads_.take_and_erase(navigation_handle)); |
| 437 // There's a chance a navigation could have started before we were added to a | 428 // There's a chance a navigation could have started before we were added to a |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 526 | 517 |
| 527 if (!committed_load_->UpdateTiming(timing)) { | 518 if (!committed_load_->UpdateTiming(timing)) { |
| 528 // If the page load tracker cannot update its timing, something is wrong | 519 // If the page load tracker cannot update its timing, something is wrong |
| 529 // with the IPC (it's from another load, or it's invalid in some other way). | 520 // with the IPC (it's from another load, or it's invalid in some other way). |
| 530 // We expect this to be a rare occurrence. | 521 // We expect this to be a rare occurrence. |
| 531 RecordInternalError(ERR_BAD_TIMING_IPC); | 522 RecordInternalError(ERR_BAD_TIMING_IPC); |
| 532 } | 523 } |
| 533 } | 524 } |
| 534 | 525 |
| 535 } // namespace page_load_metrics | 526 } // namespace page_load_metrics |
| OLD | NEW |