| 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 <algorithm> | 7 #include <algorithm> |
| 8 #include <ostream> |
| 8 #include <string> | 9 #include <string> |
| 9 #include <utility> | 10 #include <utility> |
| 10 | 11 |
| 11 #include "base/location.h" | 12 #include "base/location.h" |
| 12 #include "base/logging.h" | 13 #include "base/logging.h" |
| 13 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
| 14 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
| 15 #include "base/metrics/user_metrics.h" | 16 #include "base/metrics/user_metrics.h" |
| 16 #include "components/page_load_metrics/browser/page_load_metrics_util.h" | 17 #include "components/page_load_metrics/browser/page_load_metrics_util.h" |
| 17 #include "components/page_load_metrics/common/page_load_metrics_messages.h" | 18 #include "components/page_load_metrics/common/page_load_metrics_messages.h" |
| (...skipping 28 matching lines...) Expand all Loading... |
| 46 "PageLoad.Internal.ProvisionalAbortChainSize.SameURL"; | 47 "PageLoad.Internal.ProvisionalAbortChainSize.SameURL"; |
| 47 const char kAbortChainSizeNoCommit[] = | 48 const char kAbortChainSizeNoCommit[] = |
| 48 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; | 49 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; |
| 49 const char kClientRedirectDelayAfterPaint[] = | 50 const char kClientRedirectDelayAfterPaint[] = |
| 50 "PageLoad.Internal.ClientRedirectDelayAfterPaint"; | 51 "PageLoad.Internal.ClientRedirectDelayAfterPaint"; |
| 51 | 52 |
| 52 } // namespace internal | 53 } // namespace internal |
| 53 | 54 |
| 54 namespace { | 55 namespace { |
| 55 | 56 |
| 57 // Helper to allow use of Optional<> values in LOG() messages. |
| 58 std::ostream& operator<<(std::ostream& os, |
| 59 const base::Optional<base::TimeDelta>& opt) { |
| 60 if (opt) |
| 61 os << opt.value(); |
| 62 else |
| 63 os << "(unset)"; |
| 64 return os; |
| 65 } |
| 66 |
| 56 // The url we see from the renderer side is not always the same as what | 67 // The url we see from the renderer side is not always the same as what |
| 57 // we see from the browser side (e.g. chrome://newtab). We want to be | 68 // we see from the browser side (e.g. chrome://newtab). We want to be |
| 58 // sure here that we aren't logging UMA for internal pages. | 69 // sure here that we aren't logging UMA for internal pages. |
| 59 bool IsRelevantNavigation(content::NavigationHandle* navigation_handle, | 70 bool IsRelevantNavigation(content::NavigationHandle* navigation_handle, |
| 60 const GURL& browser_url, | 71 const GURL& browser_url, |
| 61 const std::string& mime_type) { | 72 const std::string& mime_type) { |
| 62 DCHECK(navigation_handle->HasCommitted()); | 73 DCHECK(navigation_handle->HasCommitted()); |
| 63 return navigation_handle->IsInMainFrame() && | 74 return navigation_handle->IsInMainFrame() && |
| 64 !navigation_handle->IsSamePage() && | 75 !navigation_handle->IsSamePage() && |
| 65 !navigation_handle->IsErrorPage() && | 76 !navigation_handle->IsErrorPage() && |
| 66 navigation_handle->GetURL().SchemeIsHTTPOrHTTPS() && | 77 navigation_handle->GetURL().SchemeIsHTTPOrHTTPS() && |
| 67 browser_url.SchemeIsHTTPOrHTTPS() && | 78 browser_url.SchemeIsHTTPOrHTTPS() && |
| 68 (mime_type == "text/html" || mime_type == "application/xhtml+xml"); | 79 (mime_type == "text/html" || mime_type == "application/xhtml+xml"); |
| 69 } | 80 } |
| 70 | 81 |
| 71 // If second is non-zero, first must also be non-zero and less than or equal to | 82 // If second is non-zero, first must also be non-zero and less than or equal to |
| 72 // second. | 83 // second. |
| 73 bool EventsInOrder(base::TimeDelta first, base::TimeDelta second) { | 84 bool EventsInOrder(const base::Optional<base::TimeDelta>& first, |
| 74 if (second.is_zero()) { | 85 const base::Optional<base::TimeDelta>& second) { |
| 86 if (!second) { |
| 75 return true; | 87 return true; |
| 76 } | 88 } |
| 77 return !first.is_zero() && first <= second; | 89 return first && first <= second; |
| 78 } | 90 } |
| 79 | 91 |
| 80 bool IsValidPageLoadTiming(const PageLoadTiming& timing) { | 92 bool IsValidPageLoadTiming(const PageLoadTiming& timing) { |
| 81 if (timing.IsEmpty()) | 93 if (timing.IsEmpty()) |
| 82 return false; | 94 return false; |
| 83 | 95 |
| 84 // If we have a non-empty timing, it should always have a navigation start. | 96 // If we have a non-empty timing, it should always have a navigation start. |
| 85 if (timing.navigation_start.is_null()) { | 97 if (timing.navigation_start.is_null()) { |
| 86 NOTREACHED() << "Received null navigation_start."; | 98 NOTREACHED() << "Received null navigation_start."; |
| 87 return false; | 99 return false; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 104 << " for parse_start " << timing.parse_start; | 116 << " for parse_start " << timing.parse_start; |
| 105 return false; | 117 return false; |
| 106 } | 118 } |
| 107 | 119 |
| 108 if (!EventsInOrder(timing.parse_start, timing.parse_stop)) { | 120 if (!EventsInOrder(timing.parse_start, timing.parse_stop)) { |
| 109 NOTREACHED() << "Invalid parse_start " << timing.parse_start | 121 NOTREACHED() << "Invalid parse_start " << timing.parse_start |
| 110 << " for parse_stop " << timing.parse_stop; | 122 << " for parse_stop " << timing.parse_stop; |
| 111 return false; | 123 return false; |
| 112 } | 124 } |
| 113 | 125 |
| 114 if (!timing.parse_stop.is_zero()) { | 126 if (timing.parse_stop) { |
| 115 const base::TimeDelta parse_duration = | 127 const base::TimeDelta parse_duration = |
| 116 timing.parse_stop - timing.parse_start; | 128 timing.parse_stop.value() - timing.parse_start.value(); |
| 117 if (timing.parse_blocked_on_script_load_duration > parse_duration) { | 129 if (timing.parse_blocked_on_script_load_duration > parse_duration) { |
| 118 NOTREACHED() << "Invalid parse_blocked_on_script_load_duration " | 130 NOTREACHED() << "Invalid parse_blocked_on_script_load_duration " |
| 119 << timing.parse_blocked_on_script_load_duration | 131 << timing.parse_blocked_on_script_load_duration |
| 120 << " for parse duration " << parse_duration; | 132 << " for parse duration " << parse_duration; |
| 121 return false; | 133 return false; |
| 122 } | 134 } |
| 123 } | 135 } |
| 124 | 136 |
| 125 if (timing.parse_blocked_on_script_load_from_document_write_duration > | 137 if (timing.parse_blocked_on_script_load_from_document_write_duration > |
| 126 timing.parse_blocked_on_script_load_duration) { | 138 timing.parse_blocked_on_script_load_duration) { |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 aborted_chain_size_same_url); | 217 aborted_chain_size_same_url); |
| 206 } | 218 } |
| 207 } | 219 } |
| 208 | 220 |
| 209 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer, | 221 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer, |
| 210 const PageLoadTiming& last_timing, | 222 const PageLoadTiming& last_timing, |
| 211 const PageLoadTiming& new_timing, | 223 const PageLoadTiming& new_timing, |
| 212 const PageLoadMetadata& last_metadata, | 224 const PageLoadMetadata& last_metadata, |
| 213 const PageLoadExtraInfo& extra_info) { | 225 const PageLoadExtraInfo& extra_info) { |
| 214 observer->OnTimingUpdate(new_timing, extra_info); | 226 observer->OnTimingUpdate(new_timing, extra_info); |
| 215 if (!new_timing.dom_content_loaded_event_start.is_zero() && | 227 if (new_timing.dom_content_loaded_event_start && |
| 216 last_timing.dom_content_loaded_event_start.is_zero()) | 228 !last_timing.dom_content_loaded_event_start) |
| 217 observer->OnDomContentLoadedEventStart(new_timing, extra_info); | 229 observer->OnDomContentLoadedEventStart(new_timing, extra_info); |
| 218 if (!new_timing.load_event_start.is_zero() && | 230 if (new_timing.load_event_start && !last_timing.load_event_start) |
| 219 last_timing.load_event_start.is_zero()) | |
| 220 observer->OnLoadEventStart(new_timing, extra_info); | 231 observer->OnLoadEventStart(new_timing, extra_info); |
| 221 if (!new_timing.first_layout.is_zero() && last_timing.first_layout.is_zero()) | 232 if (new_timing.first_layout && !last_timing.first_layout) |
| 222 observer->OnFirstLayout(new_timing, extra_info); | 233 observer->OnFirstLayout(new_timing, extra_info); |
| 223 if (!new_timing.first_paint.is_zero() && last_timing.first_paint.is_zero()) | 234 if (new_timing.first_paint && !last_timing.first_paint) |
| 224 observer->OnFirstPaint(new_timing, extra_info); | 235 observer->OnFirstPaint(new_timing, extra_info); |
| 225 if (!new_timing.first_text_paint.is_zero() && | 236 if (new_timing.first_text_paint && !last_timing.first_text_paint) |
| 226 last_timing.first_text_paint.is_zero()) | |
| 227 observer->OnFirstTextPaint(new_timing, extra_info); | 237 observer->OnFirstTextPaint(new_timing, extra_info); |
| 228 if (!new_timing.first_image_paint.is_zero() && | 238 if (new_timing.first_image_paint && !last_timing.first_image_paint) |
| 229 last_timing.first_image_paint.is_zero()) | |
| 230 observer->OnFirstImagePaint(new_timing, extra_info); | 239 observer->OnFirstImagePaint(new_timing, extra_info); |
| 231 if (!new_timing.first_contentful_paint.is_zero() && | 240 if (new_timing.first_contentful_paint && !last_timing.first_contentful_paint) |
| 232 last_timing.first_contentful_paint.is_zero()) | |
| 233 observer->OnFirstContentfulPaint(new_timing, extra_info); | 241 observer->OnFirstContentfulPaint(new_timing, extra_info); |
| 234 if (!new_timing.parse_start.is_zero() && last_timing.parse_start.is_zero()) | 242 if (new_timing.parse_start && !last_timing.parse_start) |
| 235 observer->OnParseStart(new_timing, extra_info); | 243 observer->OnParseStart(new_timing, extra_info); |
| 236 if (!new_timing.parse_stop.is_zero() && last_timing.parse_stop.is_zero()) | 244 if (new_timing.parse_stop && !last_timing.parse_stop) |
| 237 observer->OnParseStop(new_timing, extra_info); | 245 observer->OnParseStop(new_timing, extra_info); |
| 238 if (extra_info.metadata.behavior_flags != last_metadata.behavior_flags) | 246 if (extra_info.metadata.behavior_flags != last_metadata.behavior_flags) |
| 239 observer->OnLoadingBehaviorObserved(extra_info); | 247 observer->OnLoadingBehaviorObserved(extra_info); |
| 240 } | 248 } |
| 241 | 249 |
| 242 } // namespace | 250 } // namespace |
| 243 | 251 |
| 244 PageLoadTracker::PageLoadTracker( | 252 PageLoadTracker::PageLoadTracker( |
| 245 bool in_foreground, | 253 bool in_foreground, |
| 246 PageLoadMetricsEmbedderInterface* embedder_interface, | 254 PageLoadMetricsEmbedderInterface* embedder_interface, |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 383 | 391 |
| 384 void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) { | 392 void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) { |
| 385 for (const auto& observer : observers_) { | 393 for (const auto& observer : observers_) { |
| 386 observer->OnUserInput(event); | 394 observer->OnUserInput(event); |
| 387 } | 395 } |
| 388 } | 396 } |
| 389 | 397 |
| 390 void PageLoadTracker::NotifyClientRedirectTo( | 398 void PageLoadTracker::NotifyClientRedirectTo( |
| 391 const PageLoadTracker& destination) { | 399 const PageLoadTracker& destination) { |
| 392 base::TimeDelta redirect_delay_after_paint; | 400 base::TimeDelta redirect_delay_after_paint; |
| 393 if (!timing_.first_paint.is_zero()) { | 401 if (timing_.first_paint) { |
| 394 base::TimeTicks first_paint_time = navigation_start() + timing_.first_paint; | 402 base::TimeTicks first_paint_time = |
| 403 navigation_start() + timing_.first_paint.value(); |
| 395 if (destination.navigation_start() > first_paint_time) | 404 if (destination.navigation_start() > first_paint_time) |
| 396 redirect_delay_after_paint = | 405 redirect_delay_after_paint = |
| 397 destination.navigation_start() - first_paint_time; | 406 destination.navigation_start() - first_paint_time; |
| 398 } | 407 } |
| 399 PAGE_LOAD_HISTOGRAM(internal::kClientRedirectDelayAfterPaint, | 408 PAGE_LOAD_HISTOGRAM(internal::kClientRedirectDelayAfterPaint, |
| 400 redirect_delay_after_paint); | 409 redirect_delay_after_paint); |
| 401 } | 410 } |
| 402 | 411 |
| 403 bool PageLoadTracker::UpdateTiming(const PageLoadTiming& new_timing, | 412 bool PageLoadTracker::UpdateTiming(const PageLoadTiming& new_timing, |
| 404 const PageLoadMetadata& new_metadata) { | 413 const PageLoadMetadata& new_metadata) { |
| (...skipping 507 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 912 | 921 |
| 913 if (!committed_load_->UpdateTiming(timing, metadata)) { | 922 if (!committed_load_->UpdateTiming(timing, metadata)) { |
| 914 // If the page load tracker cannot update its timing, something is wrong | 923 // If the page load tracker cannot update its timing, something is wrong |
| 915 // with the IPC (it's from another load, or it's invalid in some other way). | 924 // with the IPC (it's from another load, or it's invalid in some other way). |
| 916 // We expect this to be a rare occurrence. | 925 // We expect this to be a rare occurrence. |
| 917 RecordInternalError(ERR_BAD_TIMING_IPC); | 926 RecordInternalError(ERR_BAD_TIMING_IPC); |
| 918 } | 927 } |
| 919 } | 928 } |
| 920 | 929 |
| 921 } // namespace page_load_metrics | 930 } // namespace page_load_metrics |
| OLD | NEW |