| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_METRICS_WEB_CONTENTS_OBSERVER_H_ | |
| 6 #define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_METRICS_WEB_CONTENTS_OBSERVER_H_ | |
| 7 | |
| 8 #include <map> | |
| 9 #include <memory> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "base/macros.h" | |
| 13 #include "base/time/time.h" | |
| 14 #include "components/page_load_metrics/browser/page_load_metrics_observer.h" | |
| 15 #include "components/page_load_metrics/common/page_load_timing.h" | |
| 16 #include "content/public/browser/render_widget_host.h" | |
| 17 #include "content/public/browser/web_contents.h" | |
| 18 #include "content/public/browser/web_contents_observer.h" | |
| 19 #include "content/public/browser/web_contents_user_data.h" | |
| 20 #include "net/base/net_errors.h" | |
| 21 #include "third_party/WebKit/public/web/WebInputEvent.h" | |
| 22 | |
| 23 namespace content { | |
| 24 class NavigationHandle; | |
| 25 class RenderFrameHost; | |
| 26 } // namespace content | |
| 27 | |
| 28 namespace IPC { | |
| 29 class Message; | |
| 30 } // namespace IPC | |
| 31 | |
| 32 namespace page_load_metrics { | |
| 33 | |
| 34 class PageLoadTracker; | |
| 35 | |
| 36 namespace internal { | |
| 37 | |
| 38 extern const char kErrorEvents[]; | |
| 39 extern const char kAbortChainSizeReload[]; | |
| 40 extern const char kAbortChainSizeForwardBack[]; | |
| 41 extern const char kAbortChainSizeNewNavigation[]; | |
| 42 extern const char kAbortChainSizeNoCommit[]; | |
| 43 extern const char kAbortChainSizeSameURL[]; | |
| 44 | |
| 45 } // namespace internal | |
| 46 | |
| 47 // These errors are internal to the page_load_metrics subsystem and do not | |
| 48 // reflect actual errors that occur during a page load. | |
| 49 // | |
| 50 // If you add elements to this enum, make sure you update the enum | |
| 51 // value in histograms.xml. Only add elements to the end to prevent | |
| 52 // inconsistencies between versions. | |
| 53 enum InternalErrorLoadEvent { | |
| 54 // A timing IPC was sent from the renderer that did not line up with previous | |
| 55 // data we've received (i.e. navigation start is different or the timing | |
| 56 // struct is somehow invalid). This error can only occur once the IPC is | |
| 57 // vetted in other ways (see other errors). | |
| 58 ERR_BAD_TIMING_IPC, | |
| 59 | |
| 60 // The following IPCs are not mutually exclusive. | |
| 61 // | |
| 62 // We received an IPC when we weren't tracking a committed load. This will | |
| 63 // often happen if we get an IPC from a bad URL scheme (that is, the renderer | |
| 64 // sent us an IPC from a navigation we don't care about). | |
| 65 ERR_IPC_WITH_NO_RELEVANT_LOAD, | |
| 66 | |
| 67 // Received a notification from a frame that has been navigated away from. | |
| 68 ERR_IPC_FROM_WRONG_FRAME, | |
| 69 | |
| 70 // We received an IPC even through the last committed url from the browser | |
| 71 // was not http/s. This can happen with the renderer sending IPCs for the | |
| 72 // new tab page. This will often come paired with | |
| 73 // ERR_IPC_WITH_NO_RELEVANT_LOAD. | |
| 74 ERR_IPC_FROM_BAD_URL_SCHEME, | |
| 75 | |
| 76 // If we track a navigation, but the renderer sends us no IPCs. This could | |
| 77 // occur if the browser filters loads less aggressively than the renderer. | |
| 78 ERR_NO_IPCS_RECEIVED, | |
| 79 | |
| 80 // Tracks frequency with which we record an abort time that occurred before | |
| 81 // navigation start. This is expected to happen in some cases (see comments in | |
| 82 // cc file for details). We use this error counter to understand how often it | |
| 83 // happens. | |
| 84 ERR_ABORT_BEFORE_NAVIGATION_START, | |
| 85 | |
| 86 // A new navigation triggers abort updates in multiple trackers in | |
| 87 // |aborted_provisional_loads_|, when usually there should only be one (the | |
| 88 // navigation that just aborted because of this one). If this happens, the | |
| 89 // latest aborted load is used to track the chain size. | |
| 90 ERR_NAVIGATION_SIGNALS_MULIPLE_ABORTED_LOADS, | |
| 91 | |
| 92 // Received user input without a relevant load. This error type is deprecated, | |
| 93 // as it is valid to receive user input without a relevant load. We leave the | |
| 94 // enum value here since it's also used in histogram recording, so it's | |
| 95 // important that we not re-use this enum entry for a different value. | |
| 96 DEPRECATED_ERR_USER_INPUT_WITH_NO_RELEVANT_LOAD, | |
| 97 | |
| 98 // A TimeTicks value in the browser process has value less than | |
| 99 // navigation_start_. This could happen if navigation_start_ was computed in | |
| 100 // renderer process and the system clock has inter process time tick skew. | |
| 101 ERR_INTER_PROCESS_TIME_TICK_SKEW, | |
| 102 | |
| 103 // Add values before this final count. | |
| 104 ERR_LAST_ENTRY, | |
| 105 }; | |
| 106 | |
| 107 // This class serves as a functional interface to various chrome// features. | |
| 108 // Impl version is defined in chrome/browser/page_load_metrics. | |
| 109 class PageLoadMetricsEmbedderInterface { | |
| 110 public: | |
| 111 virtual ~PageLoadMetricsEmbedderInterface() {} | |
| 112 virtual bool IsPrerendering(content::WebContents* web_contents) = 0; | |
| 113 virtual bool IsNewTabPageUrl(const GURL& url) = 0; | |
| 114 virtual void RegisterObservers(PageLoadTracker* metrics) = 0; | |
| 115 }; | |
| 116 | |
| 117 // This class tracks a given page load, starting from navigation start / | |
| 118 // provisional load, until a new navigation commits or the navigation fails. | |
| 119 // MetricsWebContentsObserver manages a set of provisional PageLoadTrackers, as | |
| 120 // well as a committed PageLoadTracker. | |
| 121 class PageLoadTracker { | |
| 122 public: | |
| 123 // Caller must guarantee that the embedder_interface pointer outlives this | |
| 124 // class. The PageLoadTracker must not hold on to | |
| 125 // currently_committed_load_or_null or navigation_handle beyond the scope of | |
| 126 // the constructor. | |
| 127 PageLoadTracker(bool in_foreground, | |
| 128 PageLoadMetricsEmbedderInterface* embedder_interface, | |
| 129 const GURL& currently_committed_url, | |
| 130 content::NavigationHandle* navigation_handle, | |
| 131 int aborted_chain_size, | |
| 132 int aborted_chain_size_same_url); | |
| 133 ~PageLoadTracker(); | |
| 134 void Redirect(content::NavigationHandle* navigation_handle); | |
| 135 void Commit(content::NavigationHandle* navigation_handle); | |
| 136 void FailedProvisionalLoad(content::NavigationHandle* navigation_handle); | |
| 137 void WebContentsHidden(); | |
| 138 void WebContentsShown(); | |
| 139 | |
| 140 void OnInputEvent(const blink::WebInputEvent& event); | |
| 141 | |
| 142 void NotifyClientRedirectTo(const PageLoadTracker& destination); | |
| 143 | |
| 144 // Returns true if the timing was successfully updated. | |
| 145 bool UpdateTiming(const PageLoadTiming& timing, | |
| 146 const PageLoadMetadata& metadata); | |
| 147 | |
| 148 // Signals that we should stop tracking metrics for the associated page load. | |
| 149 // We may stop tracking a page load if it doesn't meet the criteria for | |
| 150 // tracking metrics in DidFinishNavigation. | |
| 151 void StopTracking(); | |
| 152 | |
| 153 int aborted_chain_size() const { return aborted_chain_size_; } | |
| 154 int aborted_chain_size_same_url() const { | |
| 155 return aborted_chain_size_same_url_; | |
| 156 } | |
| 157 | |
| 158 UserAbortType abort_type() const { return abort_type_; } | |
| 159 base::TimeTicks abort_time() const { return abort_time_; } | |
| 160 | |
| 161 void AddObserver(std::unique_ptr<PageLoadMetricsObserver> observer); | |
| 162 | |
| 163 // If the user performs some abort-like action while we are tracking this page | |
| 164 // load, notify the tracker. Note that we may not classify this as an abort if | |
| 165 // we've already performed a first paint. | |
| 166 // is_certainly_browser_timestamp signifies if the timestamp passed is taken | |
| 167 // in the | |
| 168 // browser process or not. We need this to possibly clamp browser timestamp on | |
| 169 // a machine with inter process time tick skew. | |
| 170 void NotifyAbort(UserAbortType abort_type, | |
| 171 base::TimeTicks timestamp, | |
| 172 bool is_certainly_browser_timestamp); | |
| 173 void UpdateAbort(UserAbortType abort_type, | |
| 174 base::TimeTicks timestamp, | |
| 175 bool is_certainly_browser_timestamp); | |
| 176 | |
| 177 // This method returns true if this page load has been aborted with type of | |
| 178 // ABORT_OTHER, and the |abort_cause_time| is within a sufficiently close | |
| 179 // delta to when it was aborted. Note that only provisional loads can be | |
| 180 // aborted with ABORT_OTHER. While this heuristic is coarse, it works better | |
| 181 // and is simpler than other feasible methods. See https://goo.gl/WKRG98. | |
| 182 bool IsLikelyProvisionalAbort(base::TimeTicks abort_cause_time); | |
| 183 | |
| 184 bool MatchesOriginalNavigation(content::NavigationHandle* navigation_handle); | |
| 185 | |
| 186 // Only valid to call post-commit. | |
| 187 const GURL& committed_url() const { | |
| 188 DCHECK(!commit_time_.is_null()); | |
| 189 return url_; | |
| 190 } | |
| 191 | |
| 192 base::TimeTicks navigation_start() const { return navigation_start_; } | |
| 193 | |
| 194 PageLoadExtraInfo ComputePageLoadExtraInfo(); | |
| 195 | |
| 196 private: | |
| 197 // This function converts a TimeTicks value taken in the browser process | |
| 198 // to navigation_start_ if: | |
| 199 // - base::TimeTicks is not comparable across processes because the clock | |
| 200 // is not system wide monotonic. | |
| 201 // - *event_time < navigation_start_ | |
| 202 void ClampBrowserTimestampIfInterProcessTimeTickSkew( | |
| 203 base::TimeTicks* event_time); | |
| 204 | |
| 205 void UpdateAbortInternal(UserAbortType abort_type, | |
| 206 base::TimeTicks timestamp, | |
| 207 bool is_certainly_browser_timestamp); | |
| 208 | |
| 209 // If |final_navigation| is null, then this is an "unparented" abort chain, | |
| 210 // and represents a sequence of provisional aborts that never ends with a | |
| 211 // committed load. | |
| 212 void LogAbortChainHistograms(content::NavigationHandle* final_navigation); | |
| 213 | |
| 214 // Whether we stopped tracking this navigation after it was initiated. We may | |
| 215 // stop tracking a navigation if it doesn't meet the criteria for tracking | |
| 216 // metrics in DidFinishNavigation. | |
| 217 bool did_stop_tracking_; | |
| 218 | |
| 219 // The navigation start in TimeTicks, not the wall time reported by Blink. | |
| 220 const base::TimeTicks navigation_start_; | |
| 221 | |
| 222 // Time this page load was committed. If this page load hasn't committed, | |
| 223 // |commit_time_| will be zero. | |
| 224 base::TimeTicks commit_time_; | |
| 225 | |
| 226 // The URL of this page load. This is the provisional url before commit | |
| 227 // (before redirects), and the committed url after commit. | |
| 228 GURL url_; | |
| 229 | |
| 230 std::unique_ptr<FailedProvisionalLoadInfo> failed_provisional_load_info_; | |
| 231 | |
| 232 // Will be ABORT_NONE if we have not aborted this load yet. Otherwise will | |
| 233 // be the first abort action the user performed. | |
| 234 UserAbortType abort_type_; | |
| 235 base::TimeTicks abort_time_; | |
| 236 | |
| 237 // We record separate metrics for events that occur after a background, | |
| 238 // because metrics like layout/paint are delayed artificially | |
| 239 // when they occur in the background. | |
| 240 base::TimeTicks background_time_; | |
| 241 base::TimeTicks foreground_time_; | |
| 242 bool started_in_foreground_; | |
| 243 | |
| 244 PageLoadTiming timing_; | |
| 245 PageLoadMetadata metadata_; | |
| 246 | |
| 247 // This is a subtle member. If a provisional load A gets aborted by | |
| 248 // provisional load B, which gets aborted by C that eventually commits, then | |
| 249 // there exists an abort chain of length 2, starting at A's navigation_start. | |
| 250 // This is useful because it allows histograming abort chain lengths based on | |
| 251 // what the last load's transition type is. i.e. holding down F-5 to spam | |
| 252 // reload will produce a long chain with the RELOAD transition. | |
| 253 const int aborted_chain_size_; | |
| 254 | |
| 255 // This member counts consecutive provisional aborts that share a url. It will | |
| 256 // always be less than or equal to |aborted_chain_size_|. | |
| 257 const int aborted_chain_size_same_url_; | |
| 258 | |
| 259 // Interface to chrome features. Must outlive the class. | |
| 260 PageLoadMetricsEmbedderInterface* const embedder_interface_; | |
| 261 | |
| 262 std::vector<std::unique_ptr<PageLoadMetricsObserver>> observers_; | |
| 263 | |
| 264 DISALLOW_COPY_AND_ASSIGN(PageLoadTracker); | |
| 265 }; | |
| 266 | |
| 267 // MetricsWebContentsObserver tracks page loads and loading metrics | |
| 268 // related data based on IPC messages received from a | |
| 269 // MetricsRenderFrameObserver. | |
| 270 class MetricsWebContentsObserver | |
| 271 : public content::WebContentsObserver, | |
| 272 public content::WebContentsUserData<MetricsWebContentsObserver>, | |
| 273 public content::RenderWidgetHost::InputEventObserver { | |
| 274 public: | |
| 275 // Note that the returned metrics is owned by the web contents. | |
| 276 static MetricsWebContentsObserver* CreateForWebContents( | |
| 277 content::WebContents* web_contents, | |
| 278 std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface); | |
| 279 MetricsWebContentsObserver( | |
| 280 content::WebContents* web_contents, | |
| 281 std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface); | |
| 282 ~MetricsWebContentsObserver() override; | |
| 283 | |
| 284 // content::WebContentsObserver implementation: | |
| 285 bool OnMessageReceived(const IPC::Message& message, | |
| 286 content::RenderFrameHost* render_frame_host) override; | |
| 287 void DidStartNavigation( | |
| 288 content::NavigationHandle* navigation_handle) override; | |
| 289 void DidFinishNavigation( | |
| 290 content::NavigationHandle* navigation_handle) override; | |
| 291 void DidRedirectNavigation( | |
| 292 content::NavigationHandle* navigation_handle) override; | |
| 293 void NavigationStopped() override; | |
| 294 void OnInputEvent(const blink::WebInputEvent& event) override; | |
| 295 void WasShown() override; | |
| 296 void WasHidden() override; | |
| 297 void RenderProcessGone(base::TerminationStatus status) override; | |
| 298 void RenderViewHostChanged(content::RenderViewHost* old_host, | |
| 299 content::RenderViewHost* new_host) override; | |
| 300 | |
| 301 // This getter function is required for testing. | |
| 302 const PageLoadExtraInfo GetPageLoadExtraInfoForCommittedLoad(); | |
| 303 | |
| 304 private: | |
| 305 friend class content::WebContentsUserData<MetricsWebContentsObserver>; | |
| 306 | |
| 307 void HandleFailedNavigationForTrackedLoad( | |
| 308 content::NavigationHandle* navigation_handle, | |
| 309 std::unique_ptr<PageLoadTracker> tracker); | |
| 310 | |
| 311 void HandleCommittedNavigationForTrackedLoad( | |
| 312 content::NavigationHandle* navigation_handle, | |
| 313 std::unique_ptr<PageLoadTracker> tracker); | |
| 314 | |
| 315 // Notify all loads, provisional and committed, that we performed an action | |
| 316 // that might abort them. | |
| 317 void NotifyAbortAllLoads(UserAbortType abort_type); | |
| 318 void NotifyAbortAllLoadsWithTimestamp(UserAbortType abort_type, | |
| 319 base::TimeTicks timestamp, | |
| 320 bool is_certainly_browser_timestamp); | |
| 321 | |
| 322 // Register / Unregister input event callback to given RenderViewHost | |
| 323 void RegisterInputEventObserver(content::RenderViewHost* host); | |
| 324 void UnregisterInputEventObserver(content::RenderViewHost* host); | |
| 325 | |
| 326 // Notify aborted provisional loads that a new navigation occurred. This is | |
| 327 // used for more consistent attribution tracking for aborted provisional | |
| 328 // loads. This method returns the provisional load that was likely aborted | |
| 329 // by this navigation, to help instantiate the new PageLoadTracker. | |
| 330 std::unique_ptr<PageLoadTracker> NotifyAbortedProvisionalLoadsNewNavigation( | |
| 331 content::NavigationHandle* new_navigation); | |
| 332 | |
| 333 void OnTimingUpdated(content::RenderFrameHost*, | |
| 334 const PageLoadTiming& timing, | |
| 335 const PageLoadMetadata& metadata); | |
| 336 | |
| 337 bool ShouldTrackNavigation( | |
| 338 content::NavigationHandle* navigation_handle) const; | |
| 339 | |
| 340 // True if the web contents is currently in the foreground. | |
| 341 bool in_foreground_; | |
| 342 | |
| 343 // The PageLoadTrackers must be deleted before the |embedder_interface_|, | |
| 344 // because they hold a pointer to the |embedder_interface_|. | |
| 345 std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface_; | |
| 346 | |
| 347 // This map tracks all of the navigations ongoing that are not committed | |
| 348 // yet. Once a navigation is committed, it moves from the map to | |
| 349 // committed_load_. Note that a PageLoadTrackers NavigationHandle is only | |
| 350 // valid until commit time, when we remove it from the map. | |
| 351 std::map<content::NavigationHandle*, std::unique_ptr<PageLoadTracker>> | |
| 352 provisional_loads_; | |
| 353 | |
| 354 // Tracks aborted provisional loads for a little bit longer than usual (one | |
| 355 // more navigation commit at the max), in order to better understand how the | |
| 356 // navigation failed. This is because most provisional loads are destroyed | |
| 357 // and vanish before we get signal about what caused the abort (new | |
| 358 // navigation, stop button, etc.). | |
| 359 std::vector<std::unique_ptr<PageLoadTracker>> aborted_provisional_loads_; | |
| 360 | |
| 361 std::unique_ptr<PageLoadTracker> committed_load_; | |
| 362 | |
| 363 // Has the MWCO observed at least one navigation? | |
| 364 bool has_navigated_; | |
| 365 | |
| 366 DISALLOW_COPY_AND_ASSIGN(MetricsWebContentsObserver); | |
| 367 }; | |
| 368 | |
| 369 } // namespace page_load_metrics | |
| 370 | |
| 371 #endif // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_METRICS_WEB_CONTENTS_OBSERVER_H_ | |
| OLD | NEW |