OLD | NEW |
---|---|
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 Loading... | |
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 | |
73 namespace { | 89 namespace { |
74 | 90 |
75 // Helper to allow use of Optional<> values in LOG() messages. | 91 // Helper to allow use of Optional<> values in LOG() messages. |
76 std::ostream& operator<<(std::ostream& os, | 92 std::ostream& operator<<(std::ostream& os, |
77 const base::Optional<base::TimeDelta>& opt) { | 93 const base::Optional<base::TimeDelta>& opt) { |
78 if (opt) | 94 if (opt) |
79 os << opt.value(); | 95 os << opt.value(); |
80 else | 96 else |
81 os << "(unset)"; | 97 os << "(unset)"; |
82 return os; | 98 return os; |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
208 if (!EventsInOrder(timing.first_paint, timing.first_meaningful_paint)) { | 224 if (!EventsInOrder(timing.first_paint, timing.first_meaningful_paint)) { |
209 NOTREACHED() << "Invalid first_paint " << timing.first_paint | 225 NOTREACHED() << "Invalid first_paint " << timing.first_paint |
210 << " for first_meaningful_paint " | 226 << " for first_meaningful_paint " |
211 << timing.first_meaningful_paint; | 227 << timing.first_meaningful_paint; |
212 return false; | 228 return false; |
213 } | 229 } |
214 | 230 |
215 return true; | 231 return true; |
216 } | 232 } |
217 | 233 |
218 void RecordInternalError(InternalErrorLoadEvent event) { | |
219 UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY); | |
220 } | |
221 | |
222 void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) { | 234 void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) { |
223 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground, | 235 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground, |
224 completed_after_background); | 236 completed_after_background); |
225 } | 237 } |
226 | 238 |
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, | 239 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer, |
253 const PageLoadTiming& last_timing, | 240 const PageLoadTiming& last_timing, |
254 const PageLoadTiming& new_timing, | 241 const PageLoadTiming& new_timing, |
255 const PageLoadMetadata& last_metadata, | 242 const PageLoadMetadata& last_metadata, |
256 const PageLoadExtraInfo& extra_info) { | 243 const PageLoadExtraInfo& extra_info) { |
257 if (last_timing != new_timing) | 244 if (last_timing != new_timing) |
258 observer->OnTimingUpdate(new_timing, extra_info); | 245 observer->OnTimingUpdate(new_timing, extra_info); |
259 if (new_timing.dom_content_loaded_event_start && | 246 if (new_timing.dom_content_loaded_event_start && |
260 !last_timing.dom_content_loaded_event_start) | 247 !last_timing.dom_content_loaded_event_start) |
261 observer->OnDomContentLoadedEventStart(new_timing, extra_info); | 248 observer->OnDomContentLoadedEventStart(new_timing, extra_info); |
(...skipping 12 matching lines...) Expand all Loading... | |
274 if (new_timing.first_meaningful_paint && !last_timing.first_meaningful_paint) | 261 if (new_timing.first_meaningful_paint && !last_timing.first_meaningful_paint) |
275 observer->OnFirstMeaningfulPaint(new_timing, extra_info); | 262 observer->OnFirstMeaningfulPaint(new_timing, extra_info); |
276 if (new_timing.parse_start && !last_timing.parse_start) | 263 if (new_timing.parse_start && !last_timing.parse_start) |
277 observer->OnParseStart(new_timing, extra_info); | 264 observer->OnParseStart(new_timing, extra_info); |
278 if (new_timing.parse_stop && !last_timing.parse_stop) | 265 if (new_timing.parse_stop && !last_timing.parse_stop) |
279 observer->OnParseStop(new_timing, extra_info); | 266 observer->OnParseStop(new_timing, extra_info); |
280 if (extra_info.metadata.behavior_flags != last_metadata.behavior_flags) | 267 if (extra_info.metadata.behavior_flags != last_metadata.behavior_flags) |
281 observer->OnLoadingBehaviorObserved(extra_info); | 268 observer->OnLoadingBehaviorObserved(extra_info); |
282 } | 269 } |
283 | 270 |
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 | 271 } // namespace |
292 | 272 |
293 PageLoadTracker::PageLoadTracker( | 273 PageLoadTracker::PageLoadTracker( |
294 bool in_foreground, | 274 bool in_foreground, |
295 PageLoadMetricsEmbedderInterface* embedder_interface, | 275 PageLoadMetricsEmbedderInterface* embedder_interface, |
296 const GURL& currently_committed_url, | 276 const GURL& currently_committed_url, |
297 content::NavigationHandle* navigation_handle, | 277 content::NavigationHandle* navigation_handle, |
298 int aborted_chain_size, | 278 int aborted_chain_size, |
299 int aborted_chain_size_same_url) | 279 int aborted_chain_size_same_url) |
300 : did_stop_tracking_(false), | 280 : did_stop_tracking_(false), |
301 app_entered_background_(false), | 281 app_entered_background_(false), |
302 navigation_start_(navigation_handle->NavigationStart()), | 282 navigation_start_(navigation_handle->NavigationStart()), |
303 start_url_(navigation_handle->GetURL()), | 283 start_url_(navigation_handle->GetURL()), |
304 abort_type_(ABORT_NONE), | 284 abort_type_(ABORT_NONE), |
305 abort_user_initiated_(false), | 285 abort_user_initiated_(false), |
306 started_in_foreground_(in_foreground), | 286 started_in_foreground_(in_foreground), |
307 page_transition_(navigation_handle->GetPageTransition()), | 287 page_transition_(navigation_handle->GetPageTransition()), |
308 num_cache_requests_(0), | 288 num_cache_requests_(0), |
309 num_network_requests_(0), | 289 num_network_requests_(0), |
Charlie Harrison
2016/10/24 16:32:39
Can you retain the comment above IsNavigationUserI
Bryan McQuade
2016/10/24 20:43:34
Ah, sorry, I made IsNavigationUserInitiated a func
| |
310 user_gesture_(IsNavigationUserInitiated(navigation_handle)), | 290 user_gesture_(navigation_handle->HasUserGesture()), |
311 aborted_chain_size_(aborted_chain_size), | 291 aborted_chain_size_(aborted_chain_size), |
312 aborted_chain_size_same_url_(aborted_chain_size_same_url), | 292 aborted_chain_size_same_url_(aborted_chain_size_same_url), |
313 embedder_interface_(embedder_interface) { | 293 embedder_interface_(embedder_interface) { |
314 DCHECK(!navigation_handle->HasCommitted()); | 294 DCHECK(!navigation_handle->HasCommitted()); |
315 if (embedder_interface_->IsPrerendering( | 295 if (embedder_interface_->IsPrerendering( |
316 navigation_handle->GetWebContents())) { | 296 navigation_handle->GetWebContents())) { |
317 DCHECK(!started_in_foreground_); | 297 DCHECK(!started_in_foreground_); |
318 // For the time being, we do not track prerenders. See crbug.com/648338 for | 298 // For the time being, we do not track prerenders. See crbug.com/648338 for |
319 // details. | 299 // details. |
320 StopTracking(); | 300 StopTracking(); |
(...skipping 360 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
681 } | 661 } |
682 abort_type_ = abort_type; | 662 abort_type_ = abort_type; |
683 abort_time_ = timestamp; | 663 abort_time_ = timestamp; |
684 abort_user_initiated_ = user_initiated && abort_type != ABORT_CLIENT_REDIRECT; | 664 abort_user_initiated_ = user_initiated && abort_type != ABORT_CLIENT_REDIRECT; |
685 | 665 |
686 if (is_certainly_browser_timestamp) { | 666 if (is_certainly_browser_timestamp) { |
687 ClampBrowserTimestampIfInterProcessTimeTickSkew(&abort_time_); | 667 ClampBrowserTimestampIfInterProcessTimeTickSkew(&abort_time_); |
688 } | 668 } |
689 } | 669 } |
690 | 670 |
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 | 671 } // namespace page_load_metrics |
OLD | NEW |