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 <ostream> |
9 #include <string> | 9 #include <string> |
10 #include <utility> | 10 #include <utility> |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
271 started_in_foreground_); | 271 started_in_foreground_); |
272 } | 272 } |
273 } | 273 } |
274 | 274 |
275 PageLoadTracker::~PageLoadTracker() { | 275 PageLoadTracker::~PageLoadTracker() { |
276 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); | 276 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
277 | 277 |
278 if (info.time_to_commit && renderer_tracked() && timing_.IsEmpty()) { | 278 if (info.time_to_commit && renderer_tracked() && timing_.IsEmpty()) { |
279 RecordInternalError(ERR_NO_IPCS_RECEIVED); | 279 RecordInternalError(ERR_NO_IPCS_RECEIVED); |
280 } | 280 } |
281 // Recall that trackers that are given ABORT_UNKNOWN_NAVIGATION have their | 281 |
282 // chain length added to the next navigation. Take care not to double count | 282 // Don't include any aborts that resulted in a new navigation, as the chain |
283 // them. Also do not double count committed loads, which call this already. | 283 // length will be included in the aborter PageLoadTracker. |
284 if (commit_time_.is_null() && abort_type_ != ABORT_UNKNOWN_NAVIGATION) | 284 if (commit_time_.is_null() && abort_type_ != ABORT_UNKNOWN_NAVIGATION && |
285 abort_type_ != ABORT_RELOAD && abort_type_ != ABORT_FORWARD_BACK && | |
286 abort_type_ != ABORT_NEW_NAVIGATION) { | |
285 LogAbortChainHistograms(nullptr); | 287 LogAbortChainHistograms(nullptr); |
288 } | |
286 | 289 |
287 for (const auto& observer : observers_) { | 290 for (const auto& observer : observers_) { |
288 observer->OnComplete(timing_, info); | 291 observer->OnComplete(timing_, info); |
289 } | 292 } |
290 } | 293 } |
291 | 294 |
292 void PageLoadTracker::LogAbortChainHistograms( | 295 void PageLoadTracker::LogAbortChainHistograms( |
293 content::NavigationHandle* final_navigation) { | 296 content::NavigationHandle* final_navigation) { |
294 if (aborted_chain_size_ == 0) | 297 if (aborted_chain_size_ == 0) |
295 return; | 298 return; |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
376 LogAbortChainHistograms(navigation_handle); | 379 LogAbortChainHistograms(navigation_handle); |
377 } | 380 } |
378 | 381 |
379 void PageLoadTracker::FailedProvisionalLoad( | 382 void PageLoadTracker::FailedProvisionalLoad( |
380 content::NavigationHandle* navigation_handle) { | 383 content::NavigationHandle* navigation_handle) { |
381 for (const auto& observer : observers_) { | 384 for (const auto& observer : observers_) { |
382 observer->OnFailedProvisionalLoad(navigation_handle); | 385 observer->OnFailedProvisionalLoad(navigation_handle); |
383 } | 386 } |
384 } | 387 } |
385 | 388 |
389 // TODO(csharrison): We can get an approximation of whether the abort is user | |
390 // initiated or not from the NavigationHandle at this point. | |
391 void PageLoadTracker::WillStartNavigationRequest( | |
392 content::NavigationHandle* navigation_handle) { | |
393 page_transition_ = navigation_handle->GetPageTransition(); | |
394 } | |
395 | |
386 void PageLoadTracker::Redirect(content::NavigationHandle* navigation_handle) { | 396 void PageLoadTracker::Redirect(content::NavigationHandle* navigation_handle) { |
387 for (const auto& observer : observers_) { | 397 for (const auto& observer : observers_) { |
388 observer->OnRedirect(navigation_handle); | 398 observer->OnRedirect(navigation_handle); |
389 } | 399 } |
390 } | 400 } |
391 | 401 |
392 void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) { | 402 void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) { |
393 for (const auto& observer : observers_) { | 403 for (const auto& observer : observers_) { |
394 observer->OnUserInput(event); | 404 observer->OnUserInput(event); |
395 } | 405 } |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
524 return; | 534 return; |
525 | 535 |
526 UpdateAbortInternal(abort_type, timestamp, is_certainly_browser_timestamp); | 536 UpdateAbortInternal(abort_type, timestamp, is_certainly_browser_timestamp); |
527 } | 537 } |
528 | 538 |
529 void PageLoadTracker::UpdateAbort(UserAbortType abort_type, | 539 void PageLoadTracker::UpdateAbort(UserAbortType abort_type, |
530 base::TimeTicks timestamp, | 540 base::TimeTicks timestamp, |
531 bool is_certainly_browser_timestamp) { | 541 bool is_certainly_browser_timestamp) { |
532 DCHECK_NE(abort_type, ABORT_NONE); | 542 DCHECK_NE(abort_type, ABORT_NONE); |
533 DCHECK_NE(abort_type, ABORT_OTHER); | 543 DCHECK_NE(abort_type, ABORT_OTHER); |
534 DCHECK_EQ(abort_type_, ABORT_OTHER); | 544 DCHECK(abort_type_ == ABORT_OTHER || abort_type_ == ABORT_UNKNOWN_NAVIGATION) |
545 << "UpdateAbort called with abort_type_ = " << abort_type_; | |
535 | 546 |
536 // For some aborts (e.g. navigations), the initiated timestamp can be earlier | 547 // For some aborts (e.g. navigations), the initiated timestamp can be earlier |
537 // than the timestamp that aborted the load. Taking the minimum gives the | 548 // than the timestamp that aborted the load. Taking the minimum gives the |
538 // closest user initiated time known. | 549 // closest user initiated time known. |
539 UpdateAbortInternal(abort_type, std::min(abort_time_, timestamp), | 550 UpdateAbortInternal(abort_type, std::min(abort_time_, timestamp), |
540 is_certainly_browser_timestamp); | 551 is_certainly_browser_timestamp); |
541 } | 552 } |
542 | 553 |
543 bool PageLoadTracker::IsLikelyProvisionalAbort( | 554 bool PageLoadTracker::WasAbortedRecently(base::TimeTicks abort_cause_time) { |
544 base::TimeTicks abort_cause_time) { | |
545 // Note that |abort_cause_time - abort_time| can be negative. | 555 // Note that |abort_cause_time - abort_time| can be negative. |
546 return abort_type_ == ABORT_OTHER && | 556 return (abort_cause_time - abort_time_).InMilliseconds() < 100; |
547 (abort_cause_time - abort_time_).InMilliseconds() < 100; | |
548 } | 557 } |
549 | 558 |
550 bool PageLoadTracker::MatchesOriginalNavigation( | 559 bool PageLoadTracker::MatchesOriginalNavigation( |
551 content::NavigationHandle* navigation_handle) { | 560 content::NavigationHandle* navigation_handle) { |
552 // Neither navigation should have committed. | 561 // Neither navigation should have committed. |
553 DCHECK(!navigation_handle->HasCommitted()); | 562 DCHECK(!navigation_handle->HasCommitted()); |
554 DCHECK(commit_time_.is_null()); | 563 DCHECK(commit_time_.is_null()); |
555 return navigation_handle->GetURL() == url_; | 564 return navigation_handle->GetURL() == url_; |
556 } | 565 } |
557 | 566 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
635 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 644 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
636 bool handled = true; | 645 bool handled = true; |
637 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MetricsWebContentsObserver, message, | 646 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MetricsWebContentsObserver, message, |
638 render_frame_host) | 647 render_frame_host) |
639 IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_TimingUpdated, OnTimingUpdated) | 648 IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_TimingUpdated, OnTimingUpdated) |
640 IPC_MESSAGE_UNHANDLED(handled = false) | 649 IPC_MESSAGE_UNHANDLED(handled = false) |
641 IPC_END_MESSAGE_MAP() | 650 IPC_END_MESSAGE_MAP() |
642 return handled; | 651 return handled; |
643 } | 652 } |
644 | 653 |
654 // TODO(csharrison): The only reason DidStartNavigation is needed is for unit | |
655 // tests. Otherwise all this logic can go in WillStartNavigationRequest. | |
645 void MetricsWebContentsObserver::DidStartNavigation( | 656 void MetricsWebContentsObserver::DidStartNavigation( |
646 content::NavigationHandle* navigation_handle) { | 657 content::NavigationHandle* navigation_handle) { |
647 if (!navigation_handle->IsInMainFrame()) | 658 if (!navigation_handle->IsInMainFrame()) |
648 return; | 659 return; |
649 if (embedder_interface_->IsPrerendering(web_contents())) | 660 if (embedder_interface_->IsPrerendering(web_contents())) |
650 return; | 661 return; |
651 if (embedder_interface_->IsNewTabPageUrl(navigation_handle->GetURL())) | 662 if (embedder_interface_->IsNewTabPageUrl(navigation_handle->GetURL())) |
652 return; | 663 return; |
653 if (navigation_handle->GetURL().spec().compare(url::kAboutBlankURL) == 0) | 664 if (navigation_handle->GetURL().spec().compare(url::kAboutBlankURL) == 0) |
654 return; | 665 return; |
655 | 666 |
656 std::unique_ptr<PageLoadTracker> last_aborted = | |
657 NotifyAbortedProvisionalLoadsNewNavigation(navigation_handle); | |
658 | |
659 int chain_size_same_url = 0; | 667 int chain_size_same_url = 0; |
660 int chain_size = 0; | 668 int chain_size = 0; |
661 if (last_aborted) { | 669 if (!aborted_provisional_loads_.empty()) { |
670 PageLoadTracker* last_aborted = aborted_provisional_loads_.back().get(); | |
662 if (last_aborted->MatchesOriginalNavigation(navigation_handle)) { | 671 if (last_aborted->MatchesOriginalNavigation(navigation_handle)) { |
663 chain_size_same_url = last_aborted->aborted_chain_size_same_url() + 1; | 672 chain_size_same_url = last_aborted->aborted_chain_size_same_url() + 1; |
664 } else if (last_aborted->aborted_chain_size_same_url() > 0) { | 673 } else if (last_aborted->aborted_chain_size_same_url() > 0) { |
665 LogAbortChainSameURLHistogram( | 674 LogAbortChainSameURLHistogram( |
666 last_aborted->aborted_chain_size_same_url()); | 675 last_aborted->aborted_chain_size_same_url()); |
667 } | 676 } |
668 chain_size = last_aborted->aborted_chain_size() + 1; | 677 chain_size = last_aborted->aborted_chain_size() + 1; |
669 } | 678 } |
670 | 679 |
671 // Pass in the last committed url to the PageLoadTracker. If the MWCO has | 680 // Pass in the last committed url to the PageLoadTracker. If the MWCO has |
(...skipping 11 matching lines...) Expand all Loading... | |
683 has_navigated_ = true; | 692 has_navigated_ = true; |
684 | 693 |
685 // We can have two provisional loads in some cases. E.g. a same-site | 694 // We can have two provisional loads in some cases. E.g. a same-site |
686 // navigation can have a concurrent cross-process navigation started | 695 // navigation can have a concurrent cross-process navigation started |
687 // from the omnibox. | 696 // from the omnibox. |
688 DCHECK_GT(2ul, provisional_loads_.size()); | 697 DCHECK_GT(2ul, provisional_loads_.size()); |
689 // Passing raw pointers to observers_ and embedder_interface_ is safe because | 698 // Passing raw pointers to observers_ and embedder_interface_ is safe because |
690 // the MetricsWebContentsObserver owns them both list and they are torn down | 699 // the MetricsWebContentsObserver owns them both list and they are torn down |
691 // after the PageLoadTracker. The PageLoadTracker does not hold on to | 700 // after the PageLoadTracker. The PageLoadTracker does not hold on to |
692 // committed_load_ or navigation_handle beyond the scope of the constructor. | 701 // committed_load_ or navigation_handle beyond the scope of the constructor. |
693 provisional_loads_.insert(std::make_pair( | 702 auto it = provisional_loads_.insert(std::make_pair( |
694 navigation_handle, | 703 navigation_handle, |
695 base::WrapUnique(new PageLoadTracker( | 704 base::WrapUnique(new PageLoadTracker( |
696 in_foreground_, embedder_interface_.get(), currently_committed_url, | 705 in_foreground_, embedder_interface_.get(), currently_committed_url, |
697 navigation_handle, chain_size, chain_size_same_url)))); | 706 navigation_handle, chain_size, chain_size_same_url)))); |
707 // Don't clear the aborted provisional loads here because | |
708 // WillStartNavigationRequest will soon be called, providing a better value | |
709 // for the page transition and user gesture. | |
710 NotifyAbortedProvisionalLoadsNewNavigation(it.first->second.get()); | |
Bryan McQuade
2016/07/13 18:23:36
should we not call this and just wait to invoke it
Charlie Harrison
2016/07/13 18:58:19
It is only really for current unit tests which do
| |
711 } | |
712 | |
713 void MetricsWebContentsObserver::WillStartNavigationRequest( | |
714 content::NavigationHandle* navigation_handle) { | |
715 auto it = provisional_loads_.find(navigation_handle); | |
716 if (it == provisional_loads_.end()) | |
717 return; | |
718 it->second->WillStartNavigationRequest(navigation_handle); | |
719 NotifyAbortedProvisionalLoadsNewNavigation(it->second.get()); | |
720 aborted_provisional_loads_.clear(); | |
698 } | 721 } |
699 | 722 |
700 const PageLoadExtraInfo | 723 const PageLoadExtraInfo |
701 MetricsWebContentsObserver::GetPageLoadExtraInfoForCommittedLoad() { | 724 MetricsWebContentsObserver::GetPageLoadExtraInfoForCommittedLoad() { |
702 DCHECK(committed_load_); | 725 DCHECK(committed_load_); |
703 return committed_load_->ComputePageLoadExtraInfo(); | 726 return committed_load_->ComputePageLoadExtraInfo(); |
704 } | 727 } |
705 | 728 |
706 void MetricsWebContentsObserver::DidFinishNavigation( | 729 void MetricsWebContentsObserver::DidFinishNavigation( |
707 content::NavigationHandle* navigation_handle) { | 730 content::NavigationHandle* navigation_handle) { |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
848 } | 871 } |
849 | 872 |
850 void MetricsWebContentsObserver::NotifyAbortAllLoads(UserAbortType abort_type) { | 873 void MetricsWebContentsObserver::NotifyAbortAllLoads(UserAbortType abort_type) { |
851 NotifyAbortAllLoadsWithTimestamp(abort_type, base::TimeTicks::Now(), true); | 874 NotifyAbortAllLoadsWithTimestamp(abort_type, base::TimeTicks::Now(), true); |
852 } | 875 } |
853 | 876 |
854 void MetricsWebContentsObserver::NotifyAbortAllLoadsWithTimestamp( | 877 void MetricsWebContentsObserver::NotifyAbortAllLoadsWithTimestamp( |
855 UserAbortType abort_type, | 878 UserAbortType abort_type, |
856 base::TimeTicks timestamp, | 879 base::TimeTicks timestamp, |
857 bool is_certainly_browser_timestamp) { | 880 bool is_certainly_browser_timestamp) { |
858 if (committed_load_) | 881 if (committed_load_) { |
859 committed_load_->NotifyAbort(abort_type, timestamp, | 882 committed_load_->NotifyAbort(abort_type, timestamp, |
883 | |
860 is_certainly_browser_timestamp); | 884 is_certainly_browser_timestamp); |
885 } | |
861 for (const auto& kv : provisional_loads_) { | 886 for (const auto& kv : provisional_loads_) { |
862 kv.second->NotifyAbort(abort_type, timestamp, | 887 kv.second->NotifyAbort(abort_type, timestamp, |
863 is_certainly_browser_timestamp); | 888 is_certainly_browser_timestamp); |
864 } | 889 } |
865 for (const auto& tracker : aborted_provisional_loads_) { | 890 for (const auto& tracker : aborted_provisional_loads_) { |
866 if (tracker->IsLikelyProvisionalAbort(timestamp)) | 891 // Only update an aborted provisional load if it has an OTHER type. Do not |
892 // include UNKNOWN_NAVIGATION here because those are handled in | |
893 // NotifyAbortedProvisionalLoadsNewNavigation. | |
894 // TODO(csharrison): This is an implementation flaw due to how the unit | |
Bryan McQuade
2016/07/13 18:23:36
which tests in particular break due to this? is it
Charlie Harrison
2016/07/13 18:58:19
The tests that fail if we include UNKNOWN_NAVIGATI
| |
895 // testing framework avoids calling WillStartNavigationRequest. | |
896 if (tracker->WasAbortedRecently(timestamp) && | |
897 tracker->abort_type() == ABORT_OTHER) { | |
867 tracker->UpdateAbort(abort_type, timestamp, | 898 tracker->UpdateAbort(abort_type, timestamp, |
868 is_certainly_browser_timestamp); | 899 is_certainly_browser_timestamp); |
900 } | |
869 } | 901 } |
870 aborted_provisional_loads_.clear(); | 902 aborted_provisional_loads_.clear(); |
871 } | 903 } |
872 | 904 |
873 std::unique_ptr<PageLoadTracker> | 905 void MetricsWebContentsObserver::NotifyAbortedProvisionalLoadsNewNavigation( |
874 MetricsWebContentsObserver::NotifyAbortedProvisionalLoadsNewNavigation( | 906 PageLoadTracker* new_navigation) { |
Bryan McQuade
2016/07/13 18:23:36
nit: given that the type is now a PageLoadTracker
Charlie Harrison
2016/07/13 18:58:19
Done.
| |
875 content::NavigationHandle* new_navigation) { | |
876 // If there are multiple aborted loads that can be attributed to this one, | |
877 // just count the latest one for simplicity. Other loads will fall into the | |
878 // OTHER bucket, though there shouldn't be very many. | |
879 if (aborted_provisional_loads_.size() == 0) | 907 if (aborted_provisional_loads_.size() == 0) |
880 return nullptr; | 908 return; |
881 if (aborted_provisional_loads_.size() > 1) | 909 if (aborted_provisional_loads_.size() > 1) |
882 RecordInternalError(ERR_NAVIGATION_SIGNALS_MULIPLE_ABORTED_LOADS); | 910 RecordInternalError(ERR_NAVIGATION_SIGNALS_MULIPLE_ABORTED_LOADS); |
883 | 911 |
884 std::unique_ptr<PageLoadTracker> last_aborted_load = | 912 for (const auto& it : aborted_provisional_loads_) { |
885 std::move(aborted_provisional_loads_.back()); | 913 base::TimeTicks timestamp = new_navigation->navigation_start(); |
886 aborted_provisional_loads_.pop_back(); | 914 if (it->WasAbortedRecently(timestamp) && |
887 | 915 (it->abort_type() == ABORT_OTHER || |
888 base::TimeTicks timestamp = new_navigation->NavigationStart(); | 916 it->abort_type() == ABORT_UNKNOWN_NAVIGATION)) { |
889 if (last_aborted_load->IsLikelyProvisionalAbort(timestamp)) | 917 const base::Optional<ui::PageTransition>& transition = |
890 last_aborted_load->UpdateAbort(ABORT_UNKNOWN_NAVIGATION, timestamp, false); | 918 new_navigation->page_transition(); |
891 | 919 UserAbortType abort_type = |
892 aborted_provisional_loads_.clear(); | 920 transition ? AbortTypeForPageTransition(transition.value()) |
893 return last_aborted_load; | 921 : ABORT_UNKNOWN_NAVIGATION; |
922 it->UpdateAbort(abort_type, timestamp, false); | |
923 } | |
924 } | |
894 } | 925 } |
895 | 926 |
896 void MetricsWebContentsObserver::OnTimingUpdated( | 927 void MetricsWebContentsObserver::OnTimingUpdated( |
897 content::RenderFrameHost* render_frame_host, | 928 content::RenderFrameHost* render_frame_host, |
898 const PageLoadTiming& timing, | 929 const PageLoadTiming& timing, |
899 const PageLoadMetadata& metadata) { | 930 const PageLoadMetadata& metadata) { |
900 bool error = false; | 931 bool error = false; |
901 if (!committed_load_ || !committed_load_->renderer_tracked()) { | 932 if (!committed_load_ || !committed_load_->renderer_tracked()) { |
902 RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD); | 933 RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD); |
903 error = true; | 934 error = true; |
(...skipping 19 matching lines...) Expand all Loading... | |
923 | 954 |
924 if (!committed_load_->UpdateTiming(timing, metadata)) { | 955 if (!committed_load_->UpdateTiming(timing, metadata)) { |
925 // If the page load tracker cannot update its timing, something is wrong | 956 // If the page load tracker cannot update its timing, something is wrong |
926 // with the IPC (it's from another load, or it's invalid in some other way). | 957 // with the IPC (it's from another load, or it's invalid in some other way). |
927 // We expect this to be a rare occurrence. | 958 // We expect this to be a rare occurrence. |
928 RecordInternalError(ERR_BAD_TIMING_IPC); | 959 RecordInternalError(ERR_BAD_TIMING_IPC); |
929 } | 960 } |
930 } | 961 } |
931 | 962 |
932 } // namespace page_load_metrics | 963 } // namespace page_load_metrics |
OLD | NEW |