Chromium Code Reviews| 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 |