| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "chrome/browser/page_load_metrics/page_load_tracker.h" | 5 #include "chrome/browser/page_load_metrics/page_load_tracker.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <ostream> | 8 #include <ostream> |
| 9 #include <string> | 9 #include <string> |
| 10 #include <utility> | 10 #include <utility> |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 53 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; | 53 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; |
| 54 const char kClientRedirectFirstPaintToNavigation[] = | 54 const char kClientRedirectFirstPaintToNavigation[] = |
| 55 "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation"; | 55 "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation"; |
| 56 const char kClientRedirectWithoutPaint[] = | 56 const char kClientRedirectWithoutPaint[] = |
| 57 "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"; | 57 "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"; |
| 58 const char kPageLoadCompletedAfterAppBackground[] = | 58 const char kPageLoadCompletedAfterAppBackground[] = |
| 59 "PageLoad.Internal.PageLoadCompleted.AfterAppBackground"; | 59 "PageLoad.Internal.PageLoadCompleted.AfterAppBackground"; |
| 60 const char kPageLoadStartedInForeground[] = | 60 const char kPageLoadStartedInForeground[] = |
| 61 "PageLoad.Internal.NavigationStartedInForeground"; | 61 "PageLoad.Internal.NavigationStartedInForeground"; |
| 62 const char kPageLoadPrerender[] = "PageLoad.Internal.Prerender"; | 62 const char kPageLoadPrerender[] = "PageLoad.Internal.Prerender"; |
| 63 const char kPageLoadTimingStatus[] = "PageLoad.Internal.PageLoadTimingStatus"; |
| 63 | 64 |
| 64 } // namespace internal | 65 } // namespace internal |
| 65 | 66 |
| 66 void RecordInternalError(InternalErrorLoadEvent event) { | 67 void RecordInternalError(InternalErrorLoadEvent event) { |
| 67 UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY); | 68 UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY); |
| 68 } | 69 } |
| 69 | 70 |
| 70 // TODO(csharrison): Add a case for client side redirects, which is what JS | 71 // TODO(csharrison): Add a case for client side redirects, which is what JS |
| 71 // initiated window.location / window.history navigations get set to. | 72 // initiated window.location / window.history navigations get set to. |
| 72 PageEndReason EndReasonForPageTransition(ui::PageTransition transition) { | 73 PageEndReason EndReasonForPageTransition(ui::PageTransition transition) { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 // If second is non-zero, first must also be non-zero and less than or equal to | 120 // If second is non-zero, first must also be non-zero and less than or equal to |
| 120 // second. | 121 // second. |
| 121 bool EventsInOrder(const base::Optional<base::TimeDelta>& first, | 122 bool EventsInOrder(const base::Optional<base::TimeDelta>& first, |
| 122 const base::Optional<base::TimeDelta>& second) { | 123 const base::Optional<base::TimeDelta>& second) { |
| 123 if (!second) { | 124 if (!second) { |
| 124 return true; | 125 return true; |
| 125 } | 126 } |
| 126 return first && first <= second; | 127 return first && first <= second; |
| 127 } | 128 } |
| 128 | 129 |
| 129 bool IsValidPageLoadTiming(const PageLoadTiming& timing) { | 130 internal::PageLoadTimingStatus IsValidPageLoadTiming( |
| 131 const PageLoadTiming& timing) { |
| 130 if (timing.IsEmpty()) | 132 if (timing.IsEmpty()) |
| 131 return false; | 133 return internal::INVALID_EMPTY_TIMING; |
| 132 | 134 |
| 133 // If we have a non-empty timing, it should always have a navigation start. | 135 // If we have a non-empty timing, it should always have a navigation start. |
| 134 if (timing.navigation_start.is_null()) { | 136 if (timing.navigation_start.is_null()) { |
| 135 NOTREACHED() << "Received null navigation_start."; | 137 LOG(ERROR) << "Received null navigation_start."; |
| 136 return false; | 138 return internal::INVALID_NULL_NAVIGATION_START; |
| 137 } | 139 } |
| 138 | 140 |
| 139 // Verify proper ordering between the various timings. | 141 // Verify proper ordering between the various timings. |
| 140 | 142 |
| 141 if (!EventsInOrder(timing.response_start, timing.parse_timing.parse_start)) { | 143 if (!EventsInOrder(timing.response_start, timing.parse_timing.parse_start)) { |
| 142 // We sometimes get a zero response_start with a non-zero parse start. See | 144 // We sometimes get a zero response_start with a non-zero parse start. See |
| 143 // crbug.com/590212. | 145 // crbug.com/590212. |
| 144 NOTREACHED() << "Invalid response_start " << timing.response_start | 146 LOG(ERROR) << "Invalid response_start " << timing.response_start |
| 145 << " for parse_start " << timing.parse_timing.parse_start; | 147 << " for parse_start " << timing.parse_timing.parse_start; |
| 146 return false; | 148 return internal::INVALID_ORDER_RESPONSE_START_PARSE_START; |
| 147 } | 149 } |
| 148 | 150 |
| 149 if (!EventsInOrder(timing.parse_timing.parse_start, | 151 if (!EventsInOrder(timing.parse_timing.parse_start, |
| 150 timing.parse_timing.parse_stop)) { | 152 timing.parse_timing.parse_stop)) { |
| 151 NOTREACHED() << "Invalid parse_start " << timing.parse_timing.parse_start | 153 LOG(ERROR) << "Invalid parse_start " << timing.parse_timing.parse_start |
| 152 << " for parse_stop " << timing.parse_timing.parse_stop; | 154 << " for parse_stop " << timing.parse_timing.parse_stop; |
| 153 return false; | 155 return internal::INVALID_ORDER_PARSE_START_PARSE_STOP; |
| 154 } | 156 } |
| 155 | 157 |
| 156 if (timing.parse_timing.parse_stop) { | 158 if (timing.parse_timing.parse_stop) { |
| 157 const base::TimeDelta parse_duration = | 159 const base::TimeDelta parse_duration = |
| 158 timing.parse_timing.parse_stop.value() - | 160 timing.parse_timing.parse_stop.value() - |
| 159 timing.parse_timing.parse_start.value(); | 161 timing.parse_timing.parse_start.value(); |
| 160 if (timing.parse_timing.parse_blocked_on_script_load_duration > | 162 if (timing.parse_timing.parse_blocked_on_script_load_duration > |
| 161 parse_duration) { | 163 parse_duration) { |
| 162 NOTREACHED() << "Invalid parse_blocked_on_script_load_duration " | 164 LOG(ERROR) << "Invalid parse_blocked_on_script_load_duration " |
| 163 << timing.parse_timing.parse_blocked_on_script_load_duration | 165 << timing.parse_timing.parse_blocked_on_script_load_duration |
| 164 << " for parse duration " << parse_duration; | 166 << " for parse duration " << parse_duration; |
| 165 return false; | 167 return internal::INVALID_SCRIPT_LOAD_LONGER_THAN_PARSE; |
| 166 } | 168 } |
| 167 if (timing.parse_timing.parse_blocked_on_script_execution_duration > | 169 if (timing.parse_timing.parse_blocked_on_script_execution_duration > |
| 168 parse_duration) { | 170 parse_duration) { |
| 169 NOTREACHED() | 171 LOG(ERROR) |
| 170 << "Invalid parse_blocked_on_script_execution_duration " | 172 << "Invalid parse_blocked_on_script_execution_duration " |
| 171 << timing.parse_timing.parse_blocked_on_script_execution_duration | 173 << timing.parse_timing.parse_blocked_on_script_execution_duration |
| 172 << " for parse duration " << parse_duration; | 174 << " for parse duration " << parse_duration; |
| 173 return false; | 175 return internal::INVALID_SCRIPT_EXEC_LONGER_THAN_PARSE; |
| 174 } | 176 } |
| 175 } | 177 } |
| 176 | 178 |
| 177 if (timing.parse_timing | 179 if (timing.parse_timing |
| 178 .parse_blocked_on_script_load_from_document_write_duration > | 180 .parse_blocked_on_script_load_from_document_write_duration > |
| 179 timing.parse_timing.parse_blocked_on_script_load_duration) { | 181 timing.parse_timing.parse_blocked_on_script_load_duration) { |
| 180 NOTREACHED() | 182 LOG(ERROR) |
| 181 << "Invalid parse_blocked_on_script_load_from_document_write_duration " | 183 << "Invalid parse_blocked_on_script_load_from_document_write_duration " |
| 182 << timing.parse_timing | 184 << timing.parse_timing |
| 183 .parse_blocked_on_script_load_from_document_write_duration | 185 .parse_blocked_on_script_load_from_document_write_duration |
| 184 << " for parse_blocked_on_script_load_duration " | 186 << " for parse_blocked_on_script_load_duration " |
| 185 << timing.parse_timing.parse_blocked_on_script_load_duration; | 187 << timing.parse_timing.parse_blocked_on_script_load_duration; |
| 186 return false; | 188 return internal::INVALID_SCRIPT_LOAD_DOC_WRITE_LONGER_THAN_SCRIPT_LOAD; |
| 187 } | 189 } |
| 188 | 190 |
| 189 if (timing.parse_timing | 191 if (timing.parse_timing |
| 190 .parse_blocked_on_script_execution_from_document_write_duration > | 192 .parse_blocked_on_script_execution_from_document_write_duration > |
| 191 timing.parse_timing.parse_blocked_on_script_execution_duration) { | 193 timing.parse_timing.parse_blocked_on_script_execution_duration) { |
| 192 NOTREACHED() | 194 LOG(ERROR) |
| 193 << "Invalid " | 195 << "Invalid " |
| 194 "parse_blocked_on_script_execution_from_document_write_duration " | 196 "parse_blocked_on_script_execution_from_document_write_duration " |
| 195 << timing.parse_timing | 197 << timing.parse_timing |
| 196 .parse_blocked_on_script_execution_from_document_write_duration | 198 .parse_blocked_on_script_execution_from_document_write_duration |
| 197 << " for parse_blocked_on_script_execution_duration " | 199 << " for parse_blocked_on_script_execution_duration " |
| 198 << timing.parse_timing.parse_blocked_on_script_execution_duration; | 200 << timing.parse_timing.parse_blocked_on_script_execution_duration; |
| 199 return false; | 201 return internal::INVALID_SCRIPT_EXEC_DOC_WRITE_LONGER_THAN_SCRIPT_EXEC; |
| 200 } | 202 } |
| 201 | 203 |
| 202 if (!EventsInOrder(timing.parse_timing.parse_stop, | 204 if (!EventsInOrder(timing.parse_timing.parse_stop, |
| 203 timing.document_timing.dom_content_loaded_event_start)) { | 205 timing.document_timing.dom_content_loaded_event_start)) { |
| 204 NOTREACHED() << "Invalid parse_stop " << timing.parse_timing.parse_stop | 206 LOG(ERROR) << "Invalid parse_stop " << timing.parse_timing.parse_stop |
| 205 << " for dom_content_loaded_event_start " | 207 << " for dom_content_loaded_event_start " |
| 206 << timing.document_timing.dom_content_loaded_event_start; | 208 << timing.document_timing.dom_content_loaded_event_start; |
| 207 return false; | 209 return internal::INVALID_ORDER_PARSE_STOP_DOM_CONTENT_LOADED; |
| 208 } | 210 } |
| 209 | 211 |
| 210 if (!EventsInOrder(timing.document_timing.dom_content_loaded_event_start, | 212 if (!EventsInOrder(timing.document_timing.dom_content_loaded_event_start, |
| 211 timing.document_timing.load_event_start)) { | 213 timing.document_timing.load_event_start)) { |
| 212 NOTREACHED() << "Invalid dom_content_loaded_event_start " | 214 LOG(ERROR) << "Invalid dom_content_loaded_event_start " |
| 213 << timing.document_timing.dom_content_loaded_event_start | 215 << timing.document_timing.dom_content_loaded_event_start |
| 214 << " for load_event_start " | 216 << " for load_event_start " |
| 215 << timing.document_timing.load_event_start; | 217 << timing.document_timing.load_event_start; |
| 216 return false; | 218 return internal::INVALID_ORDER_DOM_CONTENT_LOADED_LOAD; |
| 217 } | 219 } |
| 218 | 220 |
| 219 if (!EventsInOrder(timing.parse_timing.parse_start, | 221 if (!EventsInOrder(timing.parse_timing.parse_start, |
| 220 timing.document_timing.first_layout)) { | 222 timing.document_timing.first_layout)) { |
| 221 NOTREACHED() << "Invalid parse_start " << timing.parse_timing.parse_start | 223 LOG(ERROR) << "Invalid parse_start " << timing.parse_timing.parse_start |
| 222 << " for first_layout " << timing.document_timing.first_layout; | 224 << " for first_layout " << timing.document_timing.first_layout; |
| 223 return false; | 225 return internal::INVALID_ORDER_PARSE_START_FIRST_LAYOUT; |
| 224 } | 226 } |
| 225 | 227 |
| 226 if (!EventsInOrder(timing.document_timing.first_layout, | 228 if (!EventsInOrder(timing.document_timing.first_layout, |
| 227 timing.paint_timing.first_paint)) { | 229 timing.paint_timing.first_paint)) { |
| 228 // This can happen when we process an XHTML document that doesn't contain | 230 // This can happen when we process an XHTML document that doesn't contain |
| 229 // well formed XML. See crbug.com/627607. | 231 // well formed XML. See crbug.com/627607. |
| 230 DLOG(ERROR) << "Invalid first_layout " | 232 DLOG(ERROR) << "Invalid first_layout " |
| 231 << timing.document_timing.first_layout << " for first_paint " | 233 << timing.document_timing.first_layout << " for first_paint " |
| 232 << timing.paint_timing.first_paint; | 234 << timing.paint_timing.first_paint; |
| 233 return false; | 235 return internal::INVALID_ORDER_FIRST_LAYOUT_FIRST_PAINT; |
| 234 } | 236 } |
| 235 | 237 |
| 236 if (!EventsInOrder(timing.paint_timing.first_paint, | 238 if (!EventsInOrder(timing.paint_timing.first_paint, |
| 237 timing.paint_timing.first_text_paint)) { | 239 timing.paint_timing.first_text_paint)) { |
| 238 NOTREACHED() << "Invalid first_paint " << timing.paint_timing.first_paint | 240 LOG(ERROR) << "Invalid first_paint " << timing.paint_timing.first_paint |
| 239 << " for first_text_paint " | 241 << " for first_text_paint " |
| 240 << timing.paint_timing.first_text_paint; | 242 << timing.paint_timing.first_text_paint; |
| 241 return false; | 243 return internal::INVALID_ORDER_FIRST_PAINT_FIRST_TEXT_PAINT; |
| 242 } | 244 } |
| 243 | 245 |
| 244 if (!EventsInOrder(timing.paint_timing.first_paint, | 246 if (!EventsInOrder(timing.paint_timing.first_paint, |
| 245 timing.paint_timing.first_image_paint)) { | 247 timing.paint_timing.first_image_paint)) { |
| 246 NOTREACHED() << "Invalid first_paint " << timing.paint_timing.first_paint | 248 LOG(ERROR) << "Invalid first_paint " << timing.paint_timing.first_paint |
| 247 << " for first_image_paint " | 249 << " for first_image_paint " |
| 248 << timing.paint_timing.first_image_paint; | 250 << timing.paint_timing.first_image_paint; |
| 249 return false; | 251 return internal::INVALID_ORDER_FIRST_PAINT_FIRST_IMAGE_PAINT; |
| 250 } | 252 } |
| 251 | 253 |
| 252 if (!EventsInOrder(timing.paint_timing.first_paint, | 254 if (!EventsInOrder(timing.paint_timing.first_paint, |
| 253 timing.paint_timing.first_contentful_paint)) { | 255 timing.paint_timing.first_contentful_paint)) { |
| 254 NOTREACHED() << "Invalid first_paint " << timing.paint_timing.first_paint | 256 LOG(ERROR) << "Invalid first_paint " << timing.paint_timing.first_paint |
| 255 << " for first_contentful_paint " | 257 << " for first_contentful_paint " |
| 256 << timing.paint_timing.first_contentful_paint; | 258 << timing.paint_timing.first_contentful_paint; |
| 257 return false; | 259 return internal::INVALID_ORDER_FIRST_PAINT_FIRST_CONTENTFUL_PAINT; |
| 258 } | 260 } |
| 259 | 261 |
| 260 if (!EventsInOrder(timing.paint_timing.first_paint, | 262 if (!EventsInOrder(timing.paint_timing.first_paint, |
| 261 timing.paint_timing.first_meaningful_paint)) { | 263 timing.paint_timing.first_meaningful_paint)) { |
| 262 NOTREACHED() << "Invalid first_paint " << timing.paint_timing.first_paint | 264 LOG(ERROR) << "Invalid first_paint " << timing.paint_timing.first_paint |
| 263 << " for first_meaningful_paint " | 265 << " for first_meaningful_paint " |
| 264 << timing.paint_timing.first_meaningful_paint; | 266 << timing.paint_timing.first_meaningful_paint; |
| 265 return false; | 267 return internal::INVALID_ORDER_FIRST_PAINT_FIRST_MEANINGFUL_PAINT; |
| 266 } | 268 } |
| 267 | 269 |
| 268 return true; | 270 return internal::VALID; |
| 269 } | 271 } |
| 270 | 272 |
| 271 void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) { | 273 void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) { |
| 272 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground, | 274 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground, |
| 273 completed_after_background); | 275 completed_after_background); |
| 274 } | 276 } |
| 275 | 277 |
| 276 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer, | 278 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer, |
| 277 const PageLoadTiming& last_timing, | 279 const PageLoadTiming& last_timing, |
| 278 const PageLoadTiming& new_timing, | 280 const PageLoadTiming& new_timing, |
| (...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 582 } | 584 } |
| 583 | 585 |
| 584 // Ensure flags sent previously are still present in the new metadata fields. | 586 // Ensure flags sent previously are still present in the new metadata fields. |
| 585 const bool valid_behavior_descendent = | 587 const bool valid_behavior_descendent = |
| 586 (main_frame_metadata_.behavior_flags & new_metadata.behavior_flags) == | 588 (main_frame_metadata_.behavior_flags & new_metadata.behavior_flags) == |
| 587 main_frame_metadata_.behavior_flags; | 589 main_frame_metadata_.behavior_flags; |
| 588 if (!valid_behavior_descendent) { | 590 if (!valid_behavior_descendent) { |
| 589 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_BEHAVIOR_DESCENDENT); | 591 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_BEHAVIOR_DESCENDENT); |
| 590 return; | 592 return; |
| 591 } | 593 } |
| 592 if (!IsValidPageLoadTiming(new_timing)) { | 594 internal::PageLoadTimingStatus status = IsValidPageLoadTiming(new_timing); |
| 595 UMA_HISTOGRAM_ENUMERATION(internal::kPageLoadTimingStatus, status, |
| 596 internal::LAST_PAGE_LOAD_TIMING_STATUS); |
| 597 if (status != internal::VALID) { |
| 593 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING); | 598 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING); |
| 594 return; | 599 return; |
| 595 } | 600 } |
| 596 | 601 |
| 597 DCHECK(did_commit_); // OnCommit() must be called first. | 602 DCHECK(did_commit_); // OnCommit() must be called first. |
| 598 // There are some subtle ordering constraints here. GetPageLoadMetricsInfo() | 603 // There are some subtle ordering constraints here. GetPageLoadMetricsInfo() |
| 599 // must be called before DispatchObserverTimingCallbacks, but its | 604 // must be called before DispatchObserverTimingCallbacks, but its |
| 600 // implementation depends on the state of main_frame_metadata_, so we need | 605 // implementation depends on the state of main_frame_metadata_, so we need |
| 601 // to update main_frame_metadata_ before calling GetPageLoadMetricsInfo. | 606 // to update main_frame_metadata_ before calling GetPageLoadMetricsInfo. |
| 602 // Thus, we make a copy of timing here, update timing_ and | 607 // Thus, we make a copy of timing here, update timing_ and |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 792 observer->MediaStartedPlaying(video_type, is_in_main_frame); | 797 observer->MediaStartedPlaying(video_type, is_in_main_frame); |
| 793 } | 798 } |
| 794 | 799 |
| 795 void PageLoadTracker::OnNavigationDelayComplete(base::TimeDelta scheduled_delay, | 800 void PageLoadTracker::OnNavigationDelayComplete(base::TimeDelta scheduled_delay, |
| 796 base::TimeDelta actual_delay) { | 801 base::TimeDelta actual_delay) { |
| 797 for (const auto& observer : observers_) | 802 for (const auto& observer : observers_) |
| 798 observer->OnNavigationDelayComplete(scheduled_delay, actual_delay); | 803 observer->OnNavigationDelayComplete(scheduled_delay, actual_delay); |
| 799 } | 804 } |
| 800 | 805 |
| 801 } // namespace page_load_metrics | 806 } // namespace page_load_metrics |
| OLD | NEW |