| 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> |
| 11 | 11 |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
| 14 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
| 15 #include "chrome/browser/page_load_metrics/page_load_metrics_embedder_interface.
h" | 15 #include "chrome/browser/page_load_metrics/page_load_metrics_embedder_interface.
h" |
| 16 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h" | 16 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h" |
| 17 #include "chrome/browser/prerender/prerender_contents.h" |
| 17 #include "chrome/common/page_load_metrics/page_load_timing.h" | 18 #include "chrome/common/page_load_metrics/page_load_timing.h" |
| 18 #include "content/public/browser/navigation_details.h" | 19 #include "content/public/browser/navigation_details.h" |
| 19 #include "content/public/browser/navigation_handle.h" | 20 #include "content/public/browser/navigation_handle.h" |
| 20 #include "content/public/browser/web_contents.h" | 21 #include "content/public/browser/web_contents.h" |
| 21 #include "content/public/browser/web_contents_observer.h" | 22 #include "content/public/browser/web_contents_observer.h" |
| 22 #include "content/public/common/browser_side_navigation_policy.h" | 23 #include "content/public/common/browser_side_navigation_policy.h" |
| 23 #include "ui/base/page_transition_types.h" | 24 #include "ui/base/page_transition_types.h" |
| 24 | 25 |
| 25 // This macro invokes the specified method on each observer, passing the | 26 // This macro invokes the specified method on each observer, passing the |
| 26 // variable length arguments as the method's arguments, and removes the observer | 27 // variable length arguments as the method's arguments, and removes the observer |
| (...skipping 22 matching lines...) Expand all Loading... |
| 49 const char kAbortChainSizeSameURL[] = | 50 const char kAbortChainSizeSameURL[] = |
| 50 "PageLoad.Internal.ProvisionalAbortChainSize.SameURL"; | 51 "PageLoad.Internal.ProvisionalAbortChainSize.SameURL"; |
| 51 const char kAbortChainSizeNoCommit[] = | 52 const char kAbortChainSizeNoCommit[] = |
| 52 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; | 53 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; |
| 53 const char kClientRedirectFirstPaintToNavigation[] = | 54 const char kClientRedirectFirstPaintToNavigation[] = |
| 54 "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation"; | 55 "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation"; |
| 55 const char kClientRedirectWithoutPaint[] = | 56 const char kClientRedirectWithoutPaint[] = |
| 56 "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"; | 57 "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"; |
| 57 const char kPageLoadCompletedAfterAppBackground[] = | 58 const char kPageLoadCompletedAfterAppBackground[] = |
| 58 "PageLoad.Internal.PageLoadCompleted.AfterAppBackground"; | 59 "PageLoad.Internal.PageLoadCompleted.AfterAppBackground"; |
| 60 const char kPageLoadStartedInForeground[] = |
| 61 "PageLoad.Internal.NavigationStartedInForeground"; |
| 62 const char kPageLoadPrerender[] = "PageLoad.Internal.Prerender"; |
| 59 | 63 |
| 60 } // namespace internal | 64 } // namespace internal |
| 61 | 65 |
| 62 void RecordInternalError(InternalErrorLoadEvent event) { | 66 void RecordInternalError(InternalErrorLoadEvent event) { |
| 63 UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY); | 67 UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY); |
| 64 } | 68 } |
| 65 | 69 |
| 66 // TODO(csharrison): Add a case for client side redirects, which is what JS | 70 // TODO(csharrison): Add a case for client side redirects, which is what JS |
| 67 // initiated window.location / window.history navigations get set to. | 71 // initiated window.location / window.history navigations get set to. |
| 68 PageEndReason EndReasonForPageTransition(ui::PageTransition transition) { | 72 PageEndReason EndReasonForPageTransition(ui::PageTransition transition) { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 128 | 132 |
| 129 // If we have a non-empty timing, it should always have a navigation start. | 133 // If we have a non-empty timing, it should always have a navigation start. |
| 130 if (timing.navigation_start.is_null()) { | 134 if (timing.navigation_start.is_null()) { |
| 131 NOTREACHED() << "Received null navigation_start."; | 135 NOTREACHED() << "Received null navigation_start."; |
| 132 return false; | 136 return false; |
| 133 } | 137 } |
| 134 | 138 |
| 135 // Verify proper ordering between the various timings. | 139 // Verify proper ordering between the various timings. |
| 136 | 140 |
| 137 if (!EventsInOrder(timing.response_start, timing.parse_start)) { | 141 if (!EventsInOrder(timing.response_start, timing.parse_start)) { |
| 138 // We sometimes get a zero response_start with a non-zero parse start. See | 142 NOTREACHED() << "Invalid response_start " << timing.response_start |
| 139 // crbug.com/590212. | 143 << " for parse_start " << timing.parse_start; |
| 140 DLOG(ERROR) << "Invalid response_start " << timing.response_start | |
| 141 << " for parse_start " << timing.parse_start; | |
| 142 return false; | 144 return false; |
| 143 } | 145 } |
| 144 | 146 |
| 145 if (!EventsInOrder(timing.parse_start, timing.parse_stop)) { | 147 if (!EventsInOrder(timing.parse_start, timing.parse_stop)) { |
| 146 NOTREACHED() << "Invalid parse_start " << timing.parse_start | 148 NOTREACHED() << "Invalid parse_start " << timing.parse_start |
| 147 << " for parse_stop " << timing.parse_stop; | 149 << " for parse_stop " << timing.parse_stop; |
| 148 return false; | 150 return false; |
| 149 } | 151 } |
| 150 | 152 |
| 151 if (timing.parse_stop) { | 153 if (timing.parse_stop) { |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 304 started_in_foreground_(in_foreground), | 306 started_in_foreground_(in_foreground), |
| 305 page_transition_(navigation_handle->GetPageTransition()), | 307 page_transition_(navigation_handle->GetPageTransition()), |
| 306 user_initiated_info_(user_initiated_info), | 308 user_initiated_info_(user_initiated_info), |
| 307 aborted_chain_size_(aborted_chain_size), | 309 aborted_chain_size_(aborted_chain_size), |
| 308 aborted_chain_size_same_url_(aborted_chain_size_same_url), | 310 aborted_chain_size_same_url_(aborted_chain_size_same_url), |
| 309 embedder_interface_(embedder_interface) { | 311 embedder_interface_(embedder_interface) { |
| 310 DCHECK(!navigation_handle->HasCommitted()); | 312 DCHECK(!navigation_handle->HasCommitted()); |
| 311 embedder_interface_->RegisterObservers(this); | 313 embedder_interface_->RegisterObservers(this); |
| 312 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnStart, navigation_handle, | 314 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnStart, navigation_handle, |
| 313 currently_committed_url, started_in_foreground_); | 315 currently_committed_url, started_in_foreground_); |
| 316 |
| 317 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadStartedInForeground, |
| 318 started_in_foreground_); |
| 319 const bool is_prerender = prerender::PrerenderContents::FromWebContents( |
| 320 navigation_handle->GetWebContents()) != nullptr; |
| 321 if (is_prerender) { |
| 322 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadPrerender, true); |
| 323 } |
| 314 } | 324 } |
| 315 | 325 |
| 316 PageLoadTracker::~PageLoadTracker() { | 326 PageLoadTracker::~PageLoadTracker() { |
| 317 if (app_entered_background_) { | 327 if (app_entered_background_) { |
| 318 RecordAppBackgroundPageLoadCompleted(true); | 328 RecordAppBackgroundPageLoadCompleted(true); |
| 319 } | 329 } |
| 320 | 330 |
| 321 if (did_stop_tracking_) | 331 if (did_stop_tracking_) |
| 322 return; | 332 return; |
| 323 | 333 |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 520 child_frame_metadata_.behavior_flags |= child_metadata.behavior_flags; | 530 child_frame_metadata_.behavior_flags |= child_metadata.behavior_flags; |
| 521 if (last_child_loading_behavior_flags == child_frame_metadata_.behavior_flags) | 531 if (last_child_loading_behavior_flags == child_frame_metadata_.behavior_flags) |
| 522 return; | 532 return; |
| 523 | 533 |
| 524 PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo()); | 534 PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo()); |
| 525 for (const auto& observer : observers_) { | 535 for (const auto& observer : observers_) { |
| 526 observer->OnLoadingBehaviorObserved(extra_info); | 536 observer->OnLoadingBehaviorObserved(extra_info); |
| 527 } | 537 } |
| 528 } | 538 } |
| 529 | 539 |
| 530 bool PageLoadTracker::UpdateTiming(const PageLoadTiming& new_timing, | 540 void PageLoadTracker::UpdateTiming(const PageLoadTiming& new_timing, |
| 531 const PageLoadMetadata& new_metadata) { | 541 const PageLoadMetadata& new_metadata) { |
| 532 // Throw away IPCs that are not relevant to the current navigation. | 542 // Throw away IPCs that are not relevant to the current navigation. |
| 533 // Two timing structures cannot refer to the same navigation if they indicate | 543 // Two timing structures cannot refer to the same navigation if they indicate |
| 534 // that a navigation started at different times, so a new timing struct with a | 544 // that a navigation started at different times, so a new timing struct with a |
| 535 // different start time from an earlier struct is considered invalid. | 545 // different start time from an earlier struct is considered invalid. |
| 536 bool valid_timing_descendent = | 546 const bool valid_timing_descendent = |
| 537 timing_.navigation_start.is_null() || | 547 timing_.navigation_start.is_null() || |
| 538 timing_.navigation_start == new_timing.navigation_start; | 548 timing_.navigation_start == new_timing.navigation_start; |
| 549 if (!valid_timing_descendent) { |
| 550 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING_DESCENDENT); |
| 551 return; |
| 552 } |
| 553 |
| 539 // Ensure flags sent previously are still present in the new metadata fields. | 554 // Ensure flags sent previously are still present in the new metadata fields. |
| 540 bool valid_behavior_descendent = | 555 const bool valid_behavior_descendent = |
| 541 (main_frame_metadata_.behavior_flags & new_metadata.behavior_flags) == | 556 (main_frame_metadata_.behavior_flags & new_metadata.behavior_flags) == |
| 542 main_frame_metadata_.behavior_flags; | 557 main_frame_metadata_.behavior_flags; |
| 543 if (IsValidPageLoadTiming(new_timing) && valid_timing_descendent && | 558 if (!valid_behavior_descendent) { |
| 544 valid_behavior_descendent) { | 559 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_BEHAVIOR_DESCENDENT); |
| 545 DCHECK(did_commit_); // OnCommit() must be called first. | 560 return; |
| 546 // There are some subtle ordering constraints here. GetPageLoadMetricsInfo() | 561 } |
| 547 // must be called before DispatchObserverTimingCallbacks, but its | 562 if (!IsValidPageLoadTiming(new_timing)) { |
| 548 // implementation depends on the state of main_frame_metadata_, so we need | 563 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING); |
| 549 // to update main_frame_metadata_ before calling GetPageLoadMetricsInfo. | 564 return; |
| 550 // Thus, we make a copy of timing here, update timing_ and | 565 } |
| 551 // main_frame_metadata_, and then proceed to dispatch the observer timing | |
| 552 // callbacks. | |
| 553 const PageLoadTiming last_timing = timing_; | |
| 554 timing_ = new_timing; | |
| 555 | 566 |
| 556 const PageLoadMetadata last_metadata = main_frame_metadata_; | 567 DCHECK(did_commit_); // OnCommit() must be called first. |
| 557 main_frame_metadata_ = new_metadata; | 568 // There are some subtle ordering constraints here. GetPageLoadMetricsInfo() |
| 558 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); | 569 // must be called before DispatchObserverTimingCallbacks, but its |
| 559 for (const auto& observer : observers_) { | 570 // implementation depends on the state of main_frame_metadata_, so we need |
| 560 DispatchObserverTimingCallbacks(observer.get(), last_timing, new_timing, | 571 // to update main_frame_metadata_ before calling GetPageLoadMetricsInfo. |
| 561 last_metadata, info); | 572 // Thus, we make a copy of timing here, update timing_ and |
| 562 } | 573 // main_frame_metadata_, and then proceed to dispatch the observer timing |
| 563 return true; | 574 // callbacks. |
| 575 const PageLoadTiming last_timing = timing_; |
| 576 timing_ = new_timing; |
| 577 |
| 578 const PageLoadMetadata last_metadata = main_frame_metadata_; |
| 579 main_frame_metadata_ = new_metadata; |
| 580 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
| 581 for (const auto& observer : observers_) { |
| 582 DispatchObserverTimingCallbacks(observer.get(), last_timing, new_timing, |
| 583 last_metadata, info); |
| 564 } | 584 } |
| 565 return false; | |
| 566 } | 585 } |
| 567 | 586 |
| 568 void PageLoadTracker::OnLoadedResource( | 587 void PageLoadTracker::OnLoadedResource( |
| 569 const ExtraRequestInfo& extra_request_info) { | 588 const ExtraRequestInfo& extra_request_info) { |
| 570 for (const auto& observer : observers_) { | 589 for (const auto& observer : observers_) { |
| 571 observer->OnLoadedResource(extra_request_info); | 590 observer->OnLoadedResource(extra_request_info); |
| 572 } | 591 } |
| 573 } | 592 } |
| 574 | 593 |
| 575 void PageLoadTracker::StopTracking() { | 594 void PageLoadTracker::StopTracking() { |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 737 } | 756 } |
| 738 | 757 |
| 739 void PageLoadTracker::MediaStartedPlaying( | 758 void PageLoadTracker::MediaStartedPlaying( |
| 740 const content::WebContentsObserver::MediaPlayerInfo& video_type, | 759 const content::WebContentsObserver::MediaPlayerInfo& video_type, |
| 741 bool is_in_main_frame) { | 760 bool is_in_main_frame) { |
| 742 for (const auto& observer : observers_) | 761 for (const auto& observer : observers_) |
| 743 observer->MediaStartedPlaying(video_type, is_in_main_frame); | 762 observer->MediaStartedPlaying(video_type, is_in_main_frame); |
| 744 } | 763 } |
| 745 | 764 |
| 746 } // namespace page_load_metrics | 765 } // namespace page_load_metrics |
| OLD | NEW |