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 |