Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: components/page_load_metrics/browser/metrics_web_contents_observer.cc

Issue 2132603002: [page_load_metrics] Add a NavigationThrottle for richer abort metrics (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: More browser tests Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698