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 3 // found in the LICENSE file. 4 5 #ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_TRACKER_H_ 6 #define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_TRACKER_H_ 7 8 #include <memory> 9 #include <vector> 10 11 #include "base/macros.h" 12 #include "base/optional.h" 13 #include "base/time/time.h" 14 #include "components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h" 15 #include "components/page_load_metrics/browser/page_load_metrics_observer.h" 16 #include "components/page_load_metrics/browser/page_load_metrics_observer_delegate.h" 17 #include "components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h" 18 #include "components/page_load_metrics/browser/resource_tracker.h" 19 #include "components/page_load_metrics/common/page_end_reason.h" 20 #include "components/page_load_metrics/common/page_load_timing.h" 21 #include "content/public/browser/global_request_id.h" 22 #include "content/public/browser/web_contents_observer.h" 23 #include "net/cookies/canonical_cookie.h" 24 #include "services/metrics/public/cpp/ukm_source.h" 25 #include "ui/base/page_transition_types.h" 26 #include "ui/base/scoped_visibility_tracker.h" 27 #include "ui/gfx/geometry/size.h" 28 29 class GURL; 30 31 namespace blink { 32 class WebInputEvent; 33 } // namespace blink 34 35 namespace content { 36 class NavigationHandle; 37 class WebContents; 38 } // namespace content 39 40 namespace page_load_metrics { 41 42 class PageLoadMetricsEmbedderInterface; 43 44 namespace internal { 45 46 extern const char kErrorEvents[]; 47 extern const char kAbortChainSizeReload[]; 48 extern const char kAbortChainSizeForwardBack[]; 49 extern const char kAbortChainSizeNewNavigation[]; 50 extern const char kAbortChainSizeNoCommit[]; 51 extern const char kAbortChainSizeSameURL[]; 52 extern const char kPageLoadCompletedAfterAppBackground[]; 53 extern const char kPageLoadStartedInForeground[]; 54 55 } // namespace internal 56 57 // These errors are internal to the page_load_metrics subsystem and do not 58 // reflect actual errors that occur during a page load. 59 // 60 // If you add elements to this enum, make sure you update the enum 61 // value in histograms.xml. Only add elements to the end to prevent 62 // inconsistencies between versions. 63 enum InternalErrorLoadEvent { 64 // A timing IPC was sent from the renderer that did not line up with previous 65 // data we've received (i.e. navigation start is different or the timing 66 // struct is somehow invalid). This error can only occur once the IPC is 67 // vetted in other ways (see other errors). This error is deprecated as it has 68 // been replaced by the more detailed ERR_BAD_TIMING_IPC_* error codes. 69 DEPRECATED_ERR_BAD_TIMING_IPC, 70 71 // The following IPCs are not mutually exclusive. 72 // 73 // We received an IPC when we weren't tracking a committed load. This will 74 // often happen if we get an IPC from a bad URL scheme (that is, the renderer 75 // sent us an IPC from a navigation we don't care about). 76 ERR_IPC_WITH_NO_RELEVANT_LOAD, 77 78 // Received a notification from a frame that has been navigated away from. 79 ERR_IPC_FROM_WRONG_FRAME, 80 81 // We received an IPC even through the last committed url from the browser 82 // was not http/s. This can happen with the renderer sending IPCs for the 83 // new tab page. This will often come paired with 84 // ERR_IPC_WITH_NO_RELEVANT_LOAD. 85 ERR_IPC_FROM_BAD_URL_SCHEME, 86 87 // If we track a navigation, but the renderer sends us no IPCs. This could 88 // occur if the browser filters loads less aggressively than the renderer. 89 ERR_NO_IPCS_RECEIVED, 90 91 // Tracks frequency with which we record an end time that occurred before 92 // navigation start. This is expected to happen in some cases (see comments in 93 // cc file for details). We use this error counter to understand how often it 94 // happens. 95 ERR_END_BEFORE_NAVIGATION_START, 96 97 // A new navigation triggers abort updates in multiple trackers in 98 // |aborted_provisional_loads_|, when usually there should only be one (the 99 // navigation that just aborted because of this one). If this happens, the 100 // latest aborted load is used to track the chain size. 101 ERR_NAVIGATION_SIGNALS_MULIPLE_ABORTED_LOADS, 102 103 // Received user input without a relevant load. This error type is deprecated, 104 // as it is valid to receive user input without a relevant load. We leave the 105 // enum value here since it's also used in histogram recording, so it's 106 // important that we not re-use this enum entry for a different value. 107 DEPRECATED_ERR_USER_INPUT_WITH_NO_RELEVANT_LOAD, 108 109 // A TimeTicks value in the browser process has value less than 110 // navigation_start_. This could happen if navigation_start_ was computed in 111 // renderer process and the system clock has inter process time tick skew. 112 ERR_INTER_PROCESS_TIME_TICK_SKEW, 113 114 // At the time a PageLoadTracker was destroyed, we had received neither a 115 // commit nor a failed provisional load. 116 ERR_NO_COMMIT_OR_FAILED_PROVISIONAL_LOAD, 117 118 // No page load end time was recorded for this page load. 119 ERR_NO_PAGE_LOAD_END_TIME, 120 121 // Received a timing update from a subframe (deprecated). 122 DEPRECATED_ERR_TIMING_IPC_FROM_SUBFRAME, 123 124 // A timing IPC was sent from the renderer that contained timing data which 125 // was inconsistent with our timing data for the currently committed load. 126 ERR_BAD_TIMING_IPC_INVALID_TIMING_DESCENDENT, 127 128 // A timing IPC was sent from the renderer that contained loading behavior 129 // data which was inconsistent with our loading behavior data for the 130 // currently committed load. 131 ERR_BAD_TIMING_IPC_INVALID_BEHAVIOR_DESCENDENT, 132 133 // A timing IPC was sent from the renderer that contained invalid timing data 134 // (e.g. out of order timings, or other issues). 135 ERR_BAD_TIMING_IPC_INVALID_TIMING, 136 137 // We received a navigation start for a child frame that is before the 138 // navigation start of the main frame. 139 ERR_SUBFRAME_NAVIGATION_START_BEFORE_MAIN_FRAME, 140 141 // We received an IPC from a subframe when we weren't tracking a committed 142 // load. We expect this error to happen, and track it so we can understand how 143 // frequently this case is encountered. 144 ERR_SUBFRAME_IPC_WITH_NO_RELEVANT_LOAD, 145 146 // We received browser-process reported metrics when we weren't tracking a 147 // committed load. We expect this error to happen, and track it so we can 148 // understand how frequently this case is encountered. 149 ERR_BROWSER_USAGE_WITH_NO_RELEVANT_LOAD, 150 151 // Add values before this final count. 152 ERR_LAST_ENTRY, 153 }; 154 155 // NOTE: these functions are shared by page_load_tracker.cc and 156 // metrics_web_contents_observer.cc. They are declared here to allow both files 157 // to access them. 158 void RecordInternalError(InternalErrorLoadEvent event); 159 PageEndReason EndReasonForPageTransition(ui::PageTransition transition); 160 void LogAbortChainSameURLHistogram(int aborted_chain_size_same_url); 161 bool IsNavigationUserInitiated(content::NavigationHandle* handle); 162 163 // This class tracks a given page load, starting from navigation start / 164 // provisional load, until a new navigation commits or the navigation fails. 165 // MetricsWebContentsObserver manages a set of provisional PageLoadTrackers, as 166 // well as a committed PageLoadTracker. 167 class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client, 168 public PageLoadMetricsObserverDelegate { 169 public: 170 // Caller must guarantee that the embedder_interface pointer outlives this 171 // class. The PageLoadTracker must not hold on to 172 // currently_committed_load_or_null or navigation_handle beyond the scope of 173 // the constructor. 174 PageLoadTracker(bool in_foreground, 175 PageLoadMetricsEmbedderInterface* embedder_interface, 176 const GURL& currently_committed_url, 177 bool is_first_navigation_in_web_contents, 178 content::NavigationHandle* navigation_handle, 179 UserInitiatedInfo user_initiated_info, 180 int aborted_chain_size, 181 int aborted_chain_size_same_url); 182 ~PageLoadTracker() override; 183 184 // PageLoadMetricsUpdateDispatcher::Client implementation: 185 void OnTimingChanged() override; 186 void OnSubFrameTimingChanged(content::RenderFrameHost* rfh, 187 const mojom::PageLoadTiming& timing) override; 188 void OnSubFrameRenderDataChanged( 189 content::RenderFrameHost* rfh, 190 const mojom::FrameRenderDataUpdate& render_data) override; 191 void OnMainFrameMetadataChanged() override; 192 void OnSubframeMetadataChanged(content::RenderFrameHost* rfh, 193 const mojom::FrameMetadata& metadata) override; 194 void UpdateFeaturesUsage( 195 content::RenderFrameHost* rfh, 196 const mojom::PageLoadFeatures& new_features) override; 197 void UpdateResourceDataUse( 198 content::RenderFrameHost* rfh, 199 const std::vector<mojom::ResourceDataUpdatePtr>& resources) override; 200 void OnNewDeferredResourceCounts( 201 const mojom::DeferredResourceCounts& new_deferred_resource_data) override; 202 void UpdateFrameCpuTiming(content::RenderFrameHost* rfh, 203 const mojom::CpuTiming& timing) override; 204 void OnFrameIntersectionUpdate( 205 content::RenderFrameHost* rfh, 206 const mojom::FrameIntersectionUpdate& frame_intersection_update) override; 207 void SetUpSharedMemoryForSmoothness( 208 base::ReadOnlySharedMemoryRegion shared_memory) override; 209 210 // PageLoadMetricsObserverDelegate implementation: 211 content::WebContents* GetWebContents() const override; 212 base::TimeTicks GetNavigationStart() const override; 213 const base::Optional<base::TimeDelta>& GetFirstBackgroundTime() 214 const override; 215 const base::Optional<base::TimeDelta>& GetFirstForegroundTime() 216 const override; 217 const BackForwardCacheRestore& GetBackForwardCacheRestore( 218 size_t index) const override; 219 bool StartedInForeground() const override; 220 const UserInitiatedInfo& GetUserInitiatedInfo() const override; 221 const GURL& GetUrl() const override; 222 const GURL& GetStartUrl() const override; 223 bool DidCommit() const override; 224 PageEndReason GetPageEndReason() const override; 225 const UserInitiatedInfo& GetPageEndUserInitiatedInfo() const override; 226 base::Optional<base::TimeDelta> GetPageEndTime() const override; 227 const mojom::FrameMetadata& GetMainFrameMetadata() const override; 228 const mojom::FrameMetadata& GetSubframeMetadata() const override; 229 const PageRenderData& GetPageRenderData() const override; 230 const NormalizedCLSData& GetNormalizedCLSData() const override; 231 const mojom::InputTiming& GetPageInputTiming() const override; 232 const blink::MobileFriendliness& GetMobileFriendliness() const override; 233 const PageRenderData& GetMainFrameRenderData() const override; 234 const ui::ScopedVisibilityTracker& GetVisibilityTracker() const override; 235 const ResourceTracker& GetResourceTracker() const override; 236 const LargestContentfulPaintHandler& GetLargestContentfulPaintHandler() 237 const override; 238 const LargestContentfulPaintHandler& 239 GetExperimentalLargestContentfulPaintHandler() const override; 240 ukm::SourceId GetPageUkmSourceId() const override; 241 bool IsFirstNavigationInWebContents() const override; 242 243 void Redirect(content::NavigationHandle* navigation_handle); 244 void WillProcessNavigationResponse( 245 content::NavigationHandle* navigation_handle); 246 void Commit(content::NavigationHandle* navigation_handle); 247 void DidCommitSameDocumentNavigation( 248 content::NavigationHandle* navigation_handle); 249 void DidInternalNavigationAbort(content::NavigationHandle* navigation_handle); 250 void ReadyToCommitNavigation(content::NavigationHandle* navigation_handle); 251 void DidFinishSubFrameNavigation( 252 content::NavigationHandle* navigation_handle); 253 void FailedProvisionalLoad(content::NavigationHandle* navigation_handle, 254 base::TimeTicks failed_load_time); 255 void PageHidden(); 256 void PageShown(); 257 void FrameDeleted(content::RenderFrameHost* rfh); 258 259 void OnInputEvent(const blink::WebInputEvent& event); 260 261 // Flush any buffered metrics, as part of the metrics subsystem persisting 262 // metrics as the application goes into the background. The application may be 263 // killed at any time after this method is invoked without further 264 // notification. 265 void FlushMetricsOnAppEnterBackground(); 266 267 // Replaces the |visibility_tracker_| for testing, which can mock a clock. SetVisibilityTrackerForTesting(const ui::ScopedVisibilityTracker & tracker)268 void SetVisibilityTrackerForTesting( 269 const ui::ScopedVisibilityTracker& tracker) { 270 visibility_tracker_ = tracker; 271 } 272 273 void NotifyClientRedirectTo(content::NavigationHandle* destination); 274 275 void OnLoadedResource( 276 const ExtraRequestCompleteInfo& extra_request_complete_info); 277 278 void FrameReceivedFirstUserActivation(content::RenderFrameHost* rfh); 279 void FrameDisplayStateChanged(content::RenderFrameHost* render_frame_host, 280 bool is_display_none); 281 void FrameSizeChanged(content::RenderFrameHost* render_frame_host, 282 const gfx::Size& frame_size); 283 284 void OnCookiesRead(const GURL& url, 285 const GURL& first_party_url, 286 const net::CookieList& cookie_list, 287 bool blocked_by_policy); 288 289 void OnCookieChange(const GURL& url, 290 const GURL& first_party_url, 291 const net::CanonicalCookie& cookie, 292 bool blocked_by_policy); 293 294 void OnStorageAccessed(const GURL& url, 295 const GURL& first_party_url, 296 bool blocked_by_policy, 297 StorageType access_type); 298 299 // Signals that we should stop tracking metrics for the associated page load. 300 // We may stop tracking a page load if it doesn't meet the criteria for 301 // tracking metrics in DidFinishNavigation. 302 void StopTracking(); 303 aborted_chain_size()304 int aborted_chain_size() const { return aborted_chain_size_; } aborted_chain_size_same_url()305 int aborted_chain_size_same_url() const { 306 return aborted_chain_size_same_url_; 307 } 308 page_end_reason()309 PageEndReason page_end_reason() const { return page_end_reason_; } page_end_time()310 base::TimeTicks page_end_time() const { return page_end_time_; } 311 312 void AddObserver(std::unique_ptr<PageLoadMetricsObserver> observer); 313 314 // If the user performs some abort-like action while we are tracking this page 315 // load, notify the tracker. Note that we may not classify this as an abort if 316 // we've already performed a first paint. 317 // is_certainly_browser_timestamp signifies if the timestamp passed is taken 318 // in the 319 // browser process or not. We need this to possibly clamp browser timestamp on 320 // a machine with inter process time tick skew. 321 void NotifyPageEnd(PageEndReason page_end_reason, 322 UserInitiatedInfo user_initiated_info, 323 base::TimeTicks timestamp, 324 bool is_certainly_browser_timestamp); 325 void UpdatePageEnd(PageEndReason page_end_reason, 326 UserInitiatedInfo user_initiated_info, 327 base::TimeTicks timestamp, 328 bool is_certainly_browser_timestamp); 329 330 // This method returns true if this page load has been aborted with type of 331 // END_OTHER, and the |abort_cause_time| is within a sufficiently close 332 // delta to when it was aborted. Note that only provisional loads can be 333 // aborted with END_OTHER. While this heuristic is coarse, it works better 334 // and is simpler than other feasible methods. See https://goo.gl/WKRG98. 335 bool IsLikelyProvisionalAbort(base::TimeTicks abort_cause_time) const; 336 337 bool MatchesOriginalNavigation(content::NavigationHandle* navigation_handle); 338 did_commit()339 bool did_commit() const { return did_commit_; } url()340 const GURL& url() const { return url_; } 341 navigation_start()342 base::TimeTicks navigation_start() const { return navigation_start_; } 343 page_transition()344 ui::PageTransition page_transition() const { return page_transition_; } 345 user_initiated_info()346 UserInitiatedInfo user_initiated_info() const { return user_initiated_info_; } 347 metrics_update_dispatcher()348 PageLoadMetricsUpdateDispatcher* metrics_update_dispatcher() { 349 return &metrics_update_dispatcher_; 350 } 351 352 // Whether this PageLoadTracker has a navigation GlobalRequestID that matches 353 // the given request_id. This method will return false before 354 // WillProcessNavigationResponse has been invoked, as PageLoadTracker doesn't 355 // know its GlobalRequestID until WillProcessNavigationResponse has been 356 // invoked. 357 bool HasMatchingNavigationRequestID( 358 const content::GlobalRequestID& request_id) const; 359 360 // Invoked when a media element starts playing. 361 void MediaStartedPlaying( 362 const content::WebContentsObserver::MediaPlayerInfo& video_type, 363 content::RenderFrameHost* render_frame_host); 364 365 // Informs the observers that the event corresponding to |event_key| has 366 // occurred. 367 void BroadcastEventToObservers(const void* const event_key); 368 369 void OnEnterBackForwardCache(); 370 void OnRestoreFromBackForwardCache( 371 content::NavigationHandle* navigation_handle); 372 373 // Called when the page tracked was just activated after being loaded inside a 374 // portal. 375 void DidActivatePortal(base::TimeTicks activation_time); 376 377 private: 378 // This function converts a TimeTicks value taken in the browser process 379 // to navigation_start_ if: 380 // - base::TimeTicks is not comparable across processes because the clock 381 // is not system wide monotonic. 382 // - *event_time < navigation_start_ 383 void ClampBrowserTimestampIfInterProcessTimeTickSkew( 384 base::TimeTicks* event_time); 385 386 void UpdatePageEndInternal(PageEndReason page_end_reason, 387 UserInitiatedInfo user_initiated_info, 388 base::TimeTicks timestamp, 389 bool is_certainly_browser_timestamp); 390 // If |final_navigation| is null, then this is an "unparented" abort chain, 391 // and represents a sequence of provisional aborts that never ends with a 392 // committed load. 393 void LogAbortChainHistograms(content::NavigationHandle* final_navigation); 394 395 // Whether we stopped tracking this navigation after it was initiated. We may 396 // stop tracking a navigation if it doesn't meet the criteria for tracking 397 // metrics in DidFinishNavigation. 398 bool did_stop_tracking_; 399 400 // Whether the application went into the background when this PageLoadTracker 401 // was active. This is a temporary boolean for UMA tracking. 402 bool app_entered_background_; 403 404 // The navigation start in TimeTicks, not the wall time reported by Blink. 405 const base::TimeTicks navigation_start_; 406 407 // The navigation start after the last time when back-forward cache is 408 // restored. 409 base::TimeTicks navigation_start_after_back_forward_cache_restore_; 410 411 // The most recent URL of this page load. Updated at navigation start, upon 412 // redirection, and at commit time. 413 GURL url_; 414 415 // The start URL for this page load (before redirects). 416 GURL start_url_; 417 418 ui::ScopedVisibilityTracker visibility_tracker_; 419 420 // Whether this page load committed. 421 bool did_commit_; 422 423 std::unique_ptr<FailedProvisionalLoadInfo> failed_provisional_load_info_; 424 425 // Will be END_NONE if we have not ended this load yet. Otherwise will 426 // be the first page end reason encountered. 427 PageEndReason page_end_reason_; 428 429 // Whether the page end cause for this page load was user initiated. For 430 // example, if this page load was ended by a new navigation, this field tracks 431 // whether that new navigation was user-initiated. This field is only useful 432 // if this page load's end reason is a value other than END_NONE. Note that 433 // this value is currently experimental, and is subject to change. In 434 // particular, this field is never set to true for some page end reasons, such 435 // as stop and close, since we don't yet have sufficient instrumentation to 436 // know if a stop or close was caused by a user action. 437 UserInitiatedInfo page_end_user_initiated_info_; 438 439 base::TimeTicks page_end_time_; 440 441 // We record separate metrics for events that occur after a background, 442 // because metrics like layout/paint are delayed artificially 443 // when they occur in the background. 444 base::Optional<base::TimeDelta> first_background_time_; 445 base::Optional<base::TimeDelta> first_foreground_time_; 446 std::vector<BackForwardCacheRestore> back_forward_cache_restores_; 447 const bool started_in_foreground_; 448 449 mojom::PageLoadTimingPtr last_dispatched_merged_page_timing_; 450 blink::MobileFriendliness latest_mobile_friendliness_; 451 452 ui::PageTransition page_transition_; 453 454 base::Optional<content::GlobalRequestID> navigation_request_id_; 455 456 // Whether this page load was user initiated. 457 UserInitiatedInfo user_initiated_info_; 458 459 // This is a subtle member. If a provisional load A gets aborted by 460 // provisional load B, which gets aborted by C that eventually commits, then 461 // there exists an abort chain of length 2, starting at A's navigation_start. 462 // This is useful because it allows histograming abort chain lengths based on 463 // what the last load's transition type is. i.e. holding down F-5 to spam 464 // reload will produce a long chain with the RELOAD transition. 465 const int aborted_chain_size_; 466 467 // This member counts consecutive provisional aborts that share a url. It will 468 // always be less than or equal to |aborted_chain_size_|. 469 const int aborted_chain_size_same_url_; 470 471 // Keeps track of actively loading resources on the page. 472 ResourceTracker resource_tracker_; 473 474 // Interface to chrome features. Must outlive the class. 475 PageLoadMetricsEmbedderInterface* const embedder_interface_; 476 477 std::vector<std::unique_ptr<PageLoadMetricsObserver>> observers_; 478 479 PageLoadMetricsUpdateDispatcher metrics_update_dispatcher_; 480 481 const ukm::SourceId source_id_; 482 483 content::WebContents* const web_contents_; 484 485 const bool is_first_navigation_in_web_contents_; 486 487 page_load_metrics::LargestContentfulPaintHandler 488 largest_contentful_paint_handler_; 489 page_load_metrics::LargestContentfulPaintHandler 490 experimental_largest_contentful_paint_handler_; 491 492 DISALLOW_COPY_AND_ASSIGN(PageLoadTracker); 493 }; 494 495 } // namespace page_load_metrics 496 497 #endif // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_TRACKER_H_ 498