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