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

Side by Side Diff: chrome/browser/page_load_metrics/page_load_tracker.cc

Issue 2435233002: Factor PageLoadTracker into its own header and impl files. (Closed)
Patch Set: share IsNavigationUserInitiated impl Created 4 years, 1 month 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
« no previous file with comments | « chrome/browser/page_load_metrics/page_load_tracker.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 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/metrics_web_contents_observer.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/location.h"
13 #include "base/logging.h" 12 #include "base/logging.h"
14 #include "base/memory/ptr_util.h" 13 #include "base/memory/ptr_util.h"
15 #include "base/metrics/histogram_macros.h" 14 #include "base/metrics/histogram_macros.h"
16 #include "base/metrics/user_metrics.h" 15 #include "base/metrics/user_metrics.h"
17 #include "chrome/browser/page_load_metrics/browser_page_track_decider.h" 16 #include "chrome/browser/page_load_metrics/page_load_metrics_embedder_interface. h"
18 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h" 17 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
19 #include "chrome/common/page_load_metrics/page_load_metrics_messages.h"
20 #include "chrome/common/page_load_metrics/page_load_timing.h" 18 #include "chrome/common/page_load_metrics/page_load_timing.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/navigation_details.h" 19 #include "content/public/browser/navigation_details.h"
23 #include "content/public/browser/navigation_handle.h" 20 #include "content/public/browser/navigation_handle.h"
24 #include "content/public/browser/render_frame_host.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_observer.h"
28 #include "content/public/browser/web_contents_user_data.h"
29 #include "ipc/ipc_message.h"
30 #include "ipc/ipc_message_macros.h"
31 #include "ui/base/page_transition_types.h" 21 #include "ui/base/page_transition_types.h"
32 22
33 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
34 page_load_metrics::MetricsWebContentsObserver);
35
36 // This macro invokes the specified method on each observer, passing the 23 // This macro invokes the specified method on each observer, passing the
37 // variable length arguments as the method's arguments, and removes the observer 24 // variable length arguments as the method's arguments, and removes the observer
38 // from the list of observers if the given method returns STOP_OBSERVING. 25 // from the list of observers if the given method returns STOP_OBSERVING.
39 #define INVOKE_AND_PRUNE_OBSERVERS(observers, Method, ...) \ 26 #define INVOKE_AND_PRUNE_OBSERVERS(observers, Method, ...) \
40 for (auto it = observers.begin(); it != observers.end();) { \ 27 for (auto it = observers.begin(); it != observers.end();) { \
41 if ((*it)->Method(__VA_ARGS__) == \ 28 if ((*it)->Method(__VA_ARGS__) == \
42 PageLoadMetricsObserver::STOP_OBSERVING) { \ 29 PageLoadMetricsObserver::STOP_OBSERVING) { \
43 it = observers.erase(it); \ 30 it = observers.erase(it); \
44 } else { \ 31 } else { \
45 ++it; \ 32 ++it; \
(...skipping 17 matching lines...) Expand all
63 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; 50 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit";
64 const char kClientRedirectFirstPaintToNavigation[] = 51 const char kClientRedirectFirstPaintToNavigation[] =
65 "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation"; 52 "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation";
66 const char kClientRedirectWithoutPaint[] = 53 const char kClientRedirectWithoutPaint[] =
67 "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"; 54 "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint";
68 const char kPageLoadCompletedAfterAppBackground[] = 55 const char kPageLoadCompletedAfterAppBackground[] =
69 "PageLoad.Internal.PageLoadCompleted.AfterAppBackground"; 56 "PageLoad.Internal.PageLoadCompleted.AfterAppBackground";
70 57
71 } // namespace internal 58 } // namespace internal
72 59
60 void RecordInternalError(InternalErrorLoadEvent event) {
61 UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY);
62 }
63
64 // TODO(csharrison): Add a case for client side redirects, which is what JS
65 // initiated window.location / window.history navigations get set to.
66 UserAbortType AbortTypeForPageTransition(ui::PageTransition transition) {
67 if (transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
68 return ABORT_CLIENT_REDIRECT;
69 }
70 if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD))
71 return ABORT_RELOAD;
72 if (transition & ui::PAGE_TRANSITION_FORWARD_BACK)
73 return ABORT_FORWARD_BACK;
74 if (ui::PageTransitionIsNewNavigation(transition))
75 return ABORT_NEW_NAVIGATION;
76 NOTREACHED()
77 << "AbortTypeForPageTransition received unexpected ui::PageTransition: "
78 << transition;
79 return ABORT_OTHER;
80 }
81
82 void LogAbortChainSameURLHistogram(int aborted_chain_size_same_url) {
83 if (aborted_chain_size_same_url > 0) {
84 UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeSameURL,
85 aborted_chain_size_same_url);
86 }
87 }
88
89 // TODO(crbug.com/617904): Browser initiated navigations should have
90 // HasUserGesture() set to true. Update this once we get enough data from just
91 // renderer initiated aborts.
92 bool IsNavigationUserInitiated(content::NavigationHandle* handle) {
93 return handle->HasUserGesture();
94 }
95
73 namespace { 96 namespace {
74 97
75 // Helper to allow use of Optional<> values in LOG() messages. 98 // Helper to allow use of Optional<> values in LOG() messages.
76 std::ostream& operator<<(std::ostream& os, 99 std::ostream& operator<<(std::ostream& os,
77 const base::Optional<base::TimeDelta>& opt) { 100 const base::Optional<base::TimeDelta>& opt) {
78 if (opt) 101 if (opt)
79 os << opt.value(); 102 os << opt.value();
80 else 103 else
81 os << "(unset)"; 104 os << "(unset)";
82 return os; 105 return os;
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 if (!EventsInOrder(timing.first_paint, timing.first_meaningful_paint)) { 231 if (!EventsInOrder(timing.first_paint, timing.first_meaningful_paint)) {
209 NOTREACHED() << "Invalid first_paint " << timing.first_paint 232 NOTREACHED() << "Invalid first_paint " << timing.first_paint
210 << " for first_meaningful_paint " 233 << " for first_meaningful_paint "
211 << timing.first_meaningful_paint; 234 << timing.first_meaningful_paint;
212 return false; 235 return false;
213 } 236 }
214 237
215 return true; 238 return true;
216 } 239 }
217 240
218 void RecordInternalError(InternalErrorLoadEvent event) {
219 UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY);
220 }
221
222 void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) { 241 void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) {
223 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground, 242 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground,
224 completed_after_background); 243 completed_after_background);
225 } 244 }
226 245
227 // TODO(csharrison): Add a case for client side redirects, which is what JS
228 // initiated window.location / window.history navigations get set to.
229 UserAbortType AbortTypeForPageTransition(ui::PageTransition transition) {
230 if (transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
231 return ABORT_CLIENT_REDIRECT;
232 }
233 if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD))
234 return ABORT_RELOAD;
235 if (transition & ui::PAGE_TRANSITION_FORWARD_BACK)
236 return ABORT_FORWARD_BACK;
237 if (ui::PageTransitionIsNewNavigation(transition))
238 return ABORT_NEW_NAVIGATION;
239 NOTREACHED()
240 << "AbortTypeForPageTransition received unexpected ui::PageTransition: "
241 << transition;
242 return ABORT_OTHER;
243 }
244
245 void LogAbortChainSameURLHistogram(int aborted_chain_size_same_url) {
246 if (aborted_chain_size_same_url > 0) {
247 UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeSameURL,
248 aborted_chain_size_same_url);
249 }
250 }
251
252 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer, 246 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer,
253 const PageLoadTiming& last_timing, 247 const PageLoadTiming& last_timing,
254 const PageLoadTiming& new_timing, 248 const PageLoadTiming& new_timing,
255 const PageLoadMetadata& last_metadata, 249 const PageLoadMetadata& last_metadata,
256 const PageLoadExtraInfo& extra_info) { 250 const PageLoadExtraInfo& extra_info) {
257 if (last_timing != new_timing) 251 if (last_timing != new_timing)
258 observer->OnTimingUpdate(new_timing, extra_info); 252 observer->OnTimingUpdate(new_timing, extra_info);
259 if (new_timing.dom_content_loaded_event_start && 253 if (new_timing.dom_content_loaded_event_start &&
260 !last_timing.dom_content_loaded_event_start) 254 !last_timing.dom_content_loaded_event_start)
261 observer->OnDomContentLoadedEventStart(new_timing, extra_info); 255 observer->OnDomContentLoadedEventStart(new_timing, extra_info);
(...skipping 12 matching lines...) Expand all
274 if (new_timing.first_meaningful_paint && !last_timing.first_meaningful_paint) 268 if (new_timing.first_meaningful_paint && !last_timing.first_meaningful_paint)
275 observer->OnFirstMeaningfulPaint(new_timing, extra_info); 269 observer->OnFirstMeaningfulPaint(new_timing, extra_info);
276 if (new_timing.parse_start && !last_timing.parse_start) 270 if (new_timing.parse_start && !last_timing.parse_start)
277 observer->OnParseStart(new_timing, extra_info); 271 observer->OnParseStart(new_timing, extra_info);
278 if (new_timing.parse_stop && !last_timing.parse_stop) 272 if (new_timing.parse_stop && !last_timing.parse_stop)
279 observer->OnParseStop(new_timing, extra_info); 273 observer->OnParseStop(new_timing, extra_info);
280 if (extra_info.metadata.behavior_flags != last_metadata.behavior_flags) 274 if (extra_info.metadata.behavior_flags != last_metadata.behavior_flags)
281 observer->OnLoadingBehaviorObserved(extra_info); 275 observer->OnLoadingBehaviorObserved(extra_info);
282 } 276 }
283 277
284 // TODO(crbug.com/617904): Browser initiated navigations should have
285 // HasUserGesture() set to true. Update this once we get enough data from just
286 // renderer initiated aborts.
287 bool IsNavigationUserInitiated(content::NavigationHandle* handle) {
288 return handle->HasUserGesture();
289 }
290
291 } // namespace 278 } // namespace
292 279
293 PageLoadTracker::PageLoadTracker( 280 PageLoadTracker::PageLoadTracker(
294 bool in_foreground, 281 bool in_foreground,
295 PageLoadMetricsEmbedderInterface* embedder_interface, 282 PageLoadMetricsEmbedderInterface* embedder_interface,
296 const GURL& currently_committed_url, 283 const GURL& currently_committed_url,
297 content::NavigationHandle* navigation_handle, 284 content::NavigationHandle* navigation_handle,
298 int aborted_chain_size, 285 int aborted_chain_size,
299 int aborted_chain_size_same_url) 286 int aborted_chain_size_same_url)
300 : did_stop_tracking_(false), 287 : did_stop_tracking_(false),
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after
681 } 668 }
682 abort_type_ = abort_type; 669 abort_type_ = abort_type;
683 abort_time_ = timestamp; 670 abort_time_ = timestamp;
684 abort_user_initiated_ = user_initiated && abort_type != ABORT_CLIENT_REDIRECT; 671 abort_user_initiated_ = user_initiated && abort_type != ABORT_CLIENT_REDIRECT;
685 672
686 if (is_certainly_browser_timestamp) { 673 if (is_certainly_browser_timestamp) {
687 ClampBrowserTimestampIfInterProcessTimeTickSkew(&abort_time_); 674 ClampBrowserTimestampIfInterProcessTimeTickSkew(&abort_time_);
688 } 675 }
689 } 676 }
690 677
691 // static
692 MetricsWebContentsObserver::MetricsWebContentsObserver(
693 content::WebContents* web_contents,
694 std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface)
695 : content::WebContentsObserver(web_contents),
696 in_foreground_(false),
697 embedder_interface_(std::move(embedder_interface)),
698 has_navigated_(false) {
699 RegisterInputEventObserver(web_contents->GetRenderViewHost());
700 }
701
702 MetricsWebContentsObserver* MetricsWebContentsObserver::CreateForWebContents(
703 content::WebContents* web_contents,
704 std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface) {
705 DCHECK(web_contents);
706
707 MetricsWebContentsObserver* metrics = FromWebContents(web_contents);
708 if (!metrics) {
709 metrics = new MetricsWebContentsObserver(web_contents,
710 std::move(embedder_interface));
711 web_contents->SetUserData(UserDataKey(), metrics);
712 }
713 return metrics;
714 }
715
716 MetricsWebContentsObserver::~MetricsWebContentsObserver() {
717 // TODO(csharrison): Use a more user-initiated signal for CLOSE.
718 NotifyAbortAllLoads(ABORT_CLOSE, false);
719 }
720
721 void MetricsWebContentsObserver::RegisterInputEventObserver(
722 content::RenderViewHost* host) {
723 if (host != nullptr)
724 host->GetWidget()->AddInputEventObserver(this);
725 }
726
727 void MetricsWebContentsObserver::UnregisterInputEventObserver(
728 content::RenderViewHost* host) {
729 if (host != nullptr)
730 host->GetWidget()->RemoveInputEventObserver(this);
731 }
732
733 void MetricsWebContentsObserver::RenderViewHostChanged(
734 content::RenderViewHost* old_host,
735 content::RenderViewHost* new_host) {
736 UnregisterInputEventObserver(old_host);
737 RegisterInputEventObserver(new_host);
738 }
739
740 bool MetricsWebContentsObserver::OnMessageReceived(
741 const IPC::Message& message,
742 content::RenderFrameHost* render_frame_host) {
743 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
744 bool handled = true;
745 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MetricsWebContentsObserver, message,
746 render_frame_host)
747 IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_TimingUpdated, OnTimingUpdated)
748 IPC_MESSAGE_UNHANDLED(handled = false)
749 IPC_END_MESSAGE_MAP()
750 return handled;
751 }
752
753 void MetricsWebContentsObserver::WillStartNavigationRequest(
754 content::NavigationHandle* navigation_handle) {
755 if (!navigation_handle->IsInMainFrame())
756 return;
757
758 std::unique_ptr<PageLoadTracker> last_aborted =
759 NotifyAbortedProvisionalLoadsNewNavigation(navigation_handle);
760
761 int chain_size_same_url = 0;
762 int chain_size = 0;
763 if (last_aborted) {
764 if (last_aborted->MatchesOriginalNavigation(navigation_handle)) {
765 chain_size_same_url = last_aborted->aborted_chain_size_same_url() + 1;
766 } else if (last_aborted->aborted_chain_size_same_url() > 0) {
767 LogAbortChainSameURLHistogram(
768 last_aborted->aborted_chain_size_same_url());
769 }
770 chain_size = last_aborted->aborted_chain_size() + 1;
771 }
772
773 if (!ShouldTrackNavigation(navigation_handle))
774 return;
775
776 // Pass in the last committed url to the PageLoadTracker. If the MWCO has
777 // never observed a committed load, use the last committed url from this
778 // WebContent's opener. This is more accurate than using referrers due to
779 // referrer sanitizing and origin referrers. Note that this could potentially
780 // be inaccurate if the opener has since navigated.
781 content::WebContents* opener = web_contents()->GetOpener();
782 const GURL& opener_url =
783 !has_navigated_ && opener
784 ? web_contents()->GetOpener()->GetLastCommittedURL()
785 : GURL::EmptyGURL();
786 const GURL& currently_committed_url =
787 committed_load_ ? committed_load_->committed_url() : opener_url;
788 has_navigated_ = true;
789
790 // We can have two provisional loads in some cases. E.g. a same-site
791 // navigation can have a concurrent cross-process navigation started
792 // from the omnibox.
793 DCHECK_GT(2ul, provisional_loads_.size());
794 // Passing raw pointers to observers_ and embedder_interface_ is safe because
795 // the MetricsWebContentsObserver owns them both list and they are torn down
796 // after the PageLoadTracker. The PageLoadTracker does not hold on to
797 // committed_load_ or navigation_handle beyond the scope of the constructor.
798 provisional_loads_.insert(std::make_pair(
799 navigation_handle,
800 base::MakeUnique<PageLoadTracker>(
801 in_foreground_, embedder_interface_.get(), currently_committed_url,
802 navigation_handle, chain_size, chain_size_same_url)));
803 }
804
805 void MetricsWebContentsObserver::OnRequestComplete(
806 content::ResourceType resource_type,
807 bool was_cached,
808 int net_error) {
809 // For simplicity, only count subresources. Navigations are hard to attribute
810 // here because we won't have a committed load by the time data streams in
811 // from the IO thread.
812 if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME &&
813 net_error != net::OK) {
814 return;
815 }
816 if (!committed_load_)
817 return;
818 committed_load_->OnLoadedSubresource(was_cached);
819 }
820
821 const PageLoadExtraInfo
822 MetricsWebContentsObserver::GetPageLoadExtraInfoForCommittedLoad() {
823 DCHECK(committed_load_);
824 return committed_load_->ComputePageLoadExtraInfo();
825 }
826
827 void MetricsWebContentsObserver::DidFinishNavigation(
828 content::NavigationHandle* navigation_handle) {
829 if (!navigation_handle->IsInMainFrame())
830 return;
831
832 std::unique_ptr<PageLoadTracker> finished_nav(
833 std::move(provisional_loads_[navigation_handle]));
834 provisional_loads_.erase(navigation_handle);
835
836 // Ignore same-page navigations.
837 if (navigation_handle->HasCommitted() && navigation_handle->IsSamePage()) {
838 if (finished_nav)
839 finished_nav->StopTracking();
840 return;
841 }
842
843 // Ignore internally generated aborts for navigations with HTTP responses that
844 // don't commit, such as HTTP 204 responses and downloads.
845 if (!navigation_handle->HasCommitted() &&
846 navigation_handle->GetNetErrorCode() == net::ERR_ABORTED &&
847 navigation_handle->GetResponseHeaders()) {
848 if (finished_nav)
849 finished_nav->StopTracking();
850 return;
851 }
852
853 const bool should_track =
854 finished_nav && ShouldTrackNavigation(navigation_handle);
855
856 if (finished_nav && !should_track)
857 finished_nav->StopTracking();
858
859 if (navigation_handle->HasCommitted()) {
860 // Notify other loads that they may have been aborted by this committed
861 // load. is_certainly_browser_timestamp is set to false because
862 // NavigationStart() could be set in either the renderer or browser process.
863 NotifyAbortAllLoadsWithTimestamp(
864 AbortTypeForPageTransition(navigation_handle->GetPageTransition()),
865 IsNavigationUserInitiated(navigation_handle),
866 navigation_handle->NavigationStart(), false);
867
868 if (should_track) {
869 HandleCommittedNavigationForTrackedLoad(navigation_handle,
870 std::move(finished_nav));
871 } else {
872 committed_load_.reset();
873 }
874 } else if (should_track) {
875 HandleFailedNavigationForTrackedLoad(navigation_handle,
876 std::move(finished_nav));
877 }
878 }
879
880 // Handle a pre-commit error. Navigations that result in an error page will be
881 // ignored.
882 void MetricsWebContentsObserver::HandleFailedNavigationForTrackedLoad(
883 content::NavigationHandle* navigation_handle,
884 std::unique_ptr<PageLoadTracker> tracker) {
885 tracker->FailedProvisionalLoad(navigation_handle);
886
887 net::Error error = navigation_handle->GetNetErrorCode();
888
889 // net::OK: This case occurs when the NavigationHandle finishes and reports
890 // !HasCommitted(), but reports no net::Error. This should not occur
891 // pre-PlzNavigate, but afterwards it should represent the navigation stopped
892 // by the user before it was ready to commit.
893 // net::ERR_ABORTED: An aborted provisional load has error
894 // net::ERR_ABORTED.
895 if ((error == net::OK) || (error == net::ERR_ABORTED)) {
896 tracker->NotifyAbort(ABORT_OTHER, false, base::TimeTicks::Now(), true);
897 aborted_provisional_loads_.push_back(std::move(tracker));
898 }
899 }
900
901 void MetricsWebContentsObserver::HandleCommittedNavigationForTrackedLoad(
902 content::NavigationHandle* navigation_handle,
903 std::unique_ptr<PageLoadTracker> tracker) {
904 if (!navigation_handle->HasUserGesture() &&
905 (navigation_handle->GetPageTransition() &
906 ui::PAGE_TRANSITION_CLIENT_REDIRECT) != 0 &&
907 committed_load_)
908 committed_load_->NotifyClientRedirectTo(*tracker);
909
910 committed_load_ = std::move(tracker);
911 committed_load_->Commit(navigation_handle);
912 }
913
914 void MetricsWebContentsObserver::NavigationStopped() {
915 // TODO(csharrison): Use a more user-initiated signal for STOP.
916 NotifyAbortAllLoads(ABORT_STOP, false);
917 }
918
919 void MetricsWebContentsObserver::OnInputEvent(
920 const blink::WebInputEvent& event) {
921 // Ignore browser navigation or reload which comes with type Undefined.
922 if (event.type == blink::WebInputEvent::Type::Undefined)
923 return;
924
925 if (committed_load_)
926 committed_load_->OnInputEvent(event);
927 }
928
929 void MetricsWebContentsObserver::FlushMetricsOnAppEnterBackground() {
930 // Signal to observers that we've been backgrounded, in cases where the
931 // FlushMetricsOnAppEnterBackground callback gets invoked before the
932 // associated WasHidden callback.
933 WasHidden();
934
935 if (committed_load_)
936 committed_load_->FlushMetricsOnAppEnterBackground();
937 for (const auto& kv : provisional_loads_) {
938 kv.second->FlushMetricsOnAppEnterBackground();
939 }
940 for (const auto& tracker : aborted_provisional_loads_) {
941 tracker->FlushMetricsOnAppEnterBackground();
942 }
943 }
944
945 void MetricsWebContentsObserver::DidRedirectNavigation(
946 content::NavigationHandle* navigation_handle) {
947 if (!navigation_handle->IsInMainFrame())
948 return;
949 auto it = provisional_loads_.find(navigation_handle);
950 if (it == provisional_loads_.end())
951 return;
952 it->second->Redirect(navigation_handle);
953 }
954
955 void MetricsWebContentsObserver::WasShown() {
956 if (in_foreground_)
957 return;
958 in_foreground_ = true;
959 if (committed_load_)
960 committed_load_->WebContentsShown();
961 for (const auto& kv : provisional_loads_) {
962 kv.second->WebContentsShown();
963 }
964 }
965
966 void MetricsWebContentsObserver::WasHidden() {
967 if (!in_foreground_)
968 return;
969 in_foreground_ = false;
970 if (committed_load_)
971 committed_load_->WebContentsHidden();
972 for (const auto& kv : provisional_loads_) {
973 kv.second->WebContentsHidden();
974 }
975 }
976
977 // This will occur when the process for the main RenderFrameHost exits, either
978 // normally or from a crash. We eagerly log data from the last committed load if
979 // we have one. Don't notify aborts here because this is probably not user
980 // initiated. If it is (e.g. browser shutdown), other code paths will take care
981 // of notifying.
982 void MetricsWebContentsObserver::RenderProcessGone(
983 base::TerminationStatus status) {
984 // Other code paths will be run for normal renderer shutdown. Note that we
985 // sometimes get the STILL_RUNNING value on fast shutdown.
986 if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION ||
987 status == base::TERMINATION_STATUS_STILL_RUNNING) {
988 return;
989 }
990
991 // If this is a crash, eagerly log the aborted provisional loads and the
992 // committed load. |provisional_loads_| don't need to be destroyed here
993 // because their lifetime is tied to the NavigationHandle.
994 committed_load_.reset();
995 aborted_provisional_loads_.clear();
996 }
997
998 void MetricsWebContentsObserver::NotifyAbortAllLoads(UserAbortType abort_type,
999 bool user_initiated) {
1000 NotifyAbortAllLoadsWithTimestamp(abort_type, user_initiated,
1001 base::TimeTicks::Now(), true);
1002 }
1003
1004 void MetricsWebContentsObserver::NotifyAbortAllLoadsWithTimestamp(
1005 UserAbortType abort_type,
1006 bool user_initiated,
1007 base::TimeTicks timestamp,
1008 bool is_certainly_browser_timestamp) {
1009 if (committed_load_) {
1010 committed_load_->NotifyAbort(abort_type, user_initiated, timestamp,
1011 is_certainly_browser_timestamp);
1012 }
1013 for (const auto& kv : provisional_loads_) {
1014 kv.second->NotifyAbort(abort_type, user_initiated, timestamp,
1015 is_certainly_browser_timestamp);
1016 }
1017 for (const auto& tracker : aborted_provisional_loads_) {
1018 if (tracker->IsLikelyProvisionalAbort(timestamp)) {
1019 tracker->UpdateAbort(abort_type, user_initiated, timestamp,
1020 is_certainly_browser_timestamp);
1021 }
1022 }
1023 aborted_provisional_loads_.clear();
1024 }
1025
1026 std::unique_ptr<PageLoadTracker>
1027 MetricsWebContentsObserver::NotifyAbortedProvisionalLoadsNewNavigation(
1028 content::NavigationHandle* new_navigation) {
1029 // If there are multiple aborted loads that can be attributed to this one,
1030 // just count the latest one for simplicity. Other loads will fall into the
1031 // OTHER bucket, though there shouldn't be very many.
1032 if (aborted_provisional_loads_.size() == 0)
1033 return nullptr;
1034 if (aborted_provisional_loads_.size() > 1)
1035 RecordInternalError(ERR_NAVIGATION_SIGNALS_MULIPLE_ABORTED_LOADS);
1036
1037 std::unique_ptr<PageLoadTracker> last_aborted_load =
1038 std::move(aborted_provisional_loads_.back());
1039 aborted_provisional_loads_.pop_back();
1040
1041 base::TimeTicks timestamp = new_navigation->NavigationStart();
1042 if (last_aborted_load->IsLikelyProvisionalAbort(timestamp)) {
1043 last_aborted_load->UpdateAbort(
1044 AbortTypeForPageTransition(new_navigation->GetPageTransition()),
1045 IsNavigationUserInitiated(new_navigation), timestamp, false);
1046 }
1047
1048 aborted_provisional_loads_.clear();
1049 return last_aborted_load;
1050 }
1051
1052 void MetricsWebContentsObserver::OnTimingUpdated(
1053 content::RenderFrameHost* render_frame_host,
1054 const PageLoadTiming& timing,
1055 const PageLoadMetadata& metadata) {
1056 // We may receive notifications from frames that have been navigated away
1057 // from. We simply ignore them.
1058 if (render_frame_host != web_contents()->GetMainFrame()) {
1059 RecordInternalError(ERR_IPC_FROM_WRONG_FRAME);
1060 return;
1061 }
1062
1063 // While timings arriving for the wrong frame are expected, we do not expect
1064 // any of the errors below. Thus, we track occurrences of all errors below,
1065 // rather than returning early after encountering an error.
1066
1067 bool error = false;
1068 if (!committed_load_) {
1069 RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD);
1070 error = true;
1071 }
1072
1073 if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
1074 RecordInternalError(ERR_IPC_FROM_BAD_URL_SCHEME);
1075 error = true;
1076 }
1077
1078 if (error)
1079 return;
1080
1081 if (!committed_load_->UpdateTiming(timing, metadata)) {
1082 // If the page load tracker cannot update its timing, something is wrong
1083 // with the IPC (it's from another load, or it's invalid in some other way).
1084 // We expect this to be a rare occurrence.
1085 RecordInternalError(ERR_BAD_TIMING_IPC);
1086 }
1087 }
1088
1089 bool MetricsWebContentsObserver::ShouldTrackNavigation(
1090 content::NavigationHandle* navigation_handle) const {
1091 DCHECK(navigation_handle->IsInMainFrame());
1092 DCHECK(!navigation_handle->HasCommitted() ||
1093 !navigation_handle->IsSamePage());
1094
1095 return BrowserPageTrackDecider(embedder_interface_.get(), web_contents(),
1096 navigation_handle).ShouldTrack();
1097 }
1098
1099 } // namespace page_load_metrics 678 } // namespace page_load_metrics
OLDNEW
« no previous file with comments | « chrome/browser/page_load_metrics/page_load_tracker.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698