OLD | NEW |
1 // Copyright 2016 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 #ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_TRACKER_H_ | 5 #ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_TRACKER_H_ |
6 #define CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_TRACKER_H_ | 6 #define CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_TRACKER_H_ |
7 | 7 |
8 #include <memory> | 8 #include <memory> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/macros.h" | 11 #include "base/macros.h" |
12 #include "base/optional.h" | 12 #include "base/optional.h" |
13 #include "base/time/time.h" | 13 #include "base/time/time.h" |
14 #include "chrome/browser/page_load_metrics/page_load_metrics_observer.h" | 14 #include "chrome/browser/page_load_metrics/page_load_metrics_observer.h" |
| 15 #include "chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
" |
15 #include "chrome/browser/page_load_metrics/user_input_tracker.h" | 16 #include "chrome/browser/page_load_metrics/user_input_tracker.h" |
16 #include "chrome/common/page_load_metrics/page_load_timing.h" | 17 #include "chrome/common/page_load_metrics/page_load_timing.h" |
17 #include "content/public/browser/global_request_id.h" | 18 #include "content/public/browser/global_request_id.h" |
18 #include "content/public/browser/web_contents_observer.h" | 19 #include "content/public/browser/web_contents_observer.h" |
19 #include "ui/base/page_transition_types.h" | 20 #include "ui/base/page_transition_types.h" |
20 | 21 |
21 class GURL; | 22 class GURL; |
22 | 23 |
23 namespace blink { | 24 namespace blink { |
24 class WebInputEvent; | 25 class WebInputEvent; |
25 } // namespace blink | 26 } // namespace blink |
26 | 27 |
27 namespace content { | 28 namespace content { |
28 class NavigationHandle; | 29 class NavigationHandle; |
29 class RenderFrameHost; | |
30 } // namespace content | 30 } // namespace content |
31 | 31 |
32 namespace page_load_metrics { | 32 namespace page_load_metrics { |
33 | 33 |
34 class PageLoadMetricsEmbedderInterface; | 34 class PageLoadMetricsEmbedderInterface; |
35 class PageLoadMetricsObserver; | 35 class PageLoadMetricsObserver; |
36 | 36 |
37 namespace internal { | 37 namespace internal { |
38 | 38 |
39 extern const char kErrorEvents[]; | 39 extern const char kErrorEvents[]; |
40 extern const char kAbortChainSizeReload[]; | 40 extern const char kAbortChainSizeReload[]; |
41 extern const char kAbortChainSizeForwardBack[]; | 41 extern const char kAbortChainSizeForwardBack[]; |
42 extern const char kAbortChainSizeNewNavigation[]; | 42 extern const char kAbortChainSizeNewNavigation[]; |
43 extern const char kAbortChainSizeNoCommit[]; | 43 extern const char kAbortChainSizeNoCommit[]; |
44 extern const char kAbortChainSizeSameURL[]; | 44 extern const char kAbortChainSizeSameURL[]; |
45 extern const char kPageLoadCompletedAfterAppBackground[]; | 45 extern const char kPageLoadCompletedAfterAppBackground[]; |
46 extern const char kPageLoadTimingStatus[]; | |
47 | |
48 // Used to track the status of PageLoadTimings received from the render process. | |
49 // | |
50 // If you add elements to this enum, make sure you update the enum value in | |
51 // histograms.xml. Only add elements to the end to prevent inconsistencies | |
52 // between versions. | |
53 enum PageLoadTimingStatus { | |
54 // The PageLoadTiming is valid (all data within the PageLoadTiming is | |
55 // consistent with expectations). | |
56 VALID, | |
57 | |
58 // All remaining status codes are for invalid PageLoadTimings. | |
59 | |
60 // The PageLoadTiming was empty. | |
61 INVALID_EMPTY_TIMING, | |
62 | |
63 // The PageLoadTiming had a null navigation_start. | |
64 INVALID_NULL_NAVIGATION_START, | |
65 | |
66 // Script load or execution durations in the PageLoadTiming were too long. | |
67 INVALID_SCRIPT_LOAD_LONGER_THAN_PARSE, | |
68 INVALID_SCRIPT_EXEC_LONGER_THAN_PARSE, | |
69 INVALID_SCRIPT_LOAD_DOC_WRITE_LONGER_THAN_SCRIPT_LOAD, | |
70 INVALID_SCRIPT_EXEC_DOC_WRITE_LONGER_THAN_SCRIPT_EXEC, | |
71 | |
72 // The order of two events in the PageLoadTiming was invalid. Either the first | |
73 // wasn't present when the second was present, or the second was reported as | |
74 // happening before the first. | |
75 INVALID_ORDER_RESPONSE_START_PARSE_START, | |
76 INVALID_ORDER_PARSE_START_PARSE_STOP, | |
77 INVALID_ORDER_PARSE_STOP_DOM_CONTENT_LOADED, | |
78 INVALID_ORDER_DOM_CONTENT_LOADED_LOAD, | |
79 INVALID_ORDER_PARSE_START_FIRST_LAYOUT, | |
80 INVALID_ORDER_FIRST_LAYOUT_FIRST_PAINT, | |
81 INVALID_ORDER_FIRST_PAINT_FIRST_TEXT_PAINT, | |
82 INVALID_ORDER_FIRST_PAINT_FIRST_IMAGE_PAINT, | |
83 INVALID_ORDER_FIRST_PAINT_FIRST_CONTENTFUL_PAINT, | |
84 INVALID_ORDER_FIRST_PAINT_FIRST_MEANINGFUL_PAINT, | |
85 | |
86 // New values should be added before this final entry. | |
87 LAST_PAGE_LOAD_TIMING_STATUS | |
88 }; | |
89 | 46 |
90 } // namespace internal | 47 } // namespace internal |
91 | 48 |
92 // These errors are internal to the page_load_metrics subsystem and do not | 49 // These errors are internal to the page_load_metrics subsystem and do not |
93 // reflect actual errors that occur during a page load. | 50 // reflect actual errors that occur during a page load. |
94 // | 51 // |
95 // If you add elements to this enum, make sure you update the enum | 52 // If you add elements to this enum, make sure you update the enum |
96 // value in histograms.xml. Only add elements to the end to prevent | 53 // value in histograms.xml. Only add elements to the end to prevent |
97 // inconsistencies between versions. | 54 // inconsistencies between versions. |
98 enum InternalErrorLoadEvent { | 55 enum InternalErrorLoadEvent { |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
182 // to access them. | 139 // to access them. |
183 void RecordInternalError(InternalErrorLoadEvent event); | 140 void RecordInternalError(InternalErrorLoadEvent event); |
184 PageEndReason EndReasonForPageTransition(ui::PageTransition transition); | 141 PageEndReason EndReasonForPageTransition(ui::PageTransition transition); |
185 void LogAbortChainSameURLHistogram(int aborted_chain_size_same_url); | 142 void LogAbortChainSameURLHistogram(int aborted_chain_size_same_url); |
186 bool IsNavigationUserInitiated(content::NavigationHandle* handle); | 143 bool IsNavigationUserInitiated(content::NavigationHandle* handle); |
187 | 144 |
188 // This class tracks a given page load, starting from navigation start / | 145 // This class tracks a given page load, starting from navigation start / |
189 // provisional load, until a new navigation commits or the navigation fails. | 146 // provisional load, until a new navigation commits or the navigation fails. |
190 // MetricsWebContentsObserver manages a set of provisional PageLoadTrackers, as | 147 // MetricsWebContentsObserver manages a set of provisional PageLoadTrackers, as |
191 // well as a committed PageLoadTracker. | 148 // well as a committed PageLoadTracker. |
192 class PageLoadTracker { | 149 class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client { |
193 public: | 150 public: |
194 // Caller must guarantee that the embedder_interface pointer outlives this | 151 // Caller must guarantee that the embedder_interface pointer outlives this |
195 // class. The PageLoadTracker must not hold on to | 152 // class. The PageLoadTracker must not hold on to |
196 // currently_committed_load_or_null or navigation_handle beyond the scope of | 153 // currently_committed_load_or_null or navigation_handle beyond the scope of |
197 // the constructor. | 154 // the constructor. |
198 PageLoadTracker(bool in_foreground, | 155 PageLoadTracker(bool in_foreground, |
199 PageLoadMetricsEmbedderInterface* embedder_interface, | 156 PageLoadMetricsEmbedderInterface* embedder_interface, |
200 const GURL& currently_committed_url, | 157 const GURL& currently_committed_url, |
201 content::NavigationHandle* navigation_handle, | 158 content::NavigationHandle* navigation_handle, |
202 UserInitiatedInfo user_initiated_info, | 159 UserInitiatedInfo user_initiated_info, |
203 int aborted_chain_size, | 160 int aborted_chain_size, |
204 int aborted_chain_size_same_url); | 161 int aborted_chain_size_same_url); |
205 ~PageLoadTracker(); | 162 ~PageLoadTracker() override; |
| 163 |
| 164 // PageLoadMetricsUpdateDispatcher::Client implementation: |
| 165 void OnTimingChanged() override; |
| 166 void OnMainFrameMetadataChanged() override; |
| 167 void OnSubframeMetadataChanged() override; |
| 168 |
206 void Redirect(content::NavigationHandle* navigation_handle); | 169 void Redirect(content::NavigationHandle* navigation_handle); |
207 void WillProcessNavigationResponse( | 170 void WillProcessNavigationResponse( |
208 content::NavigationHandle* navigation_handle); | 171 content::NavigationHandle* navigation_handle); |
209 void Commit(content::NavigationHandle* navigation_handle); | 172 void Commit(content::NavigationHandle* navigation_handle); |
210 void DidCommitSameDocumentNavigation( | 173 void DidCommitSameDocumentNavigation( |
211 content::NavigationHandle* navigation_handle); | 174 content::NavigationHandle* navigation_handle); |
212 void DidFinishSubFrameNavigation( | 175 void DidFinishSubFrameNavigation( |
213 content::NavigationHandle* navigation_handle); | 176 content::NavigationHandle* navigation_handle); |
214 void FailedProvisionalLoad(content::NavigationHandle* navigation_handle, | 177 void FailedProvisionalLoad(content::NavigationHandle* navigation_handle, |
215 base::TimeTicks failed_load_time); | 178 base::TimeTicks failed_load_time); |
216 void WebContentsHidden(); | 179 void WebContentsHidden(); |
217 void WebContentsShown(); | 180 void WebContentsShown(); |
218 | 181 |
219 void OnInputEvent(const blink::WebInputEvent& event); | 182 void OnInputEvent(const blink::WebInputEvent& event); |
220 | 183 |
221 // Flush any buffered metrics, as part of the metrics subsystem persisting | 184 // Flush any buffered metrics, as part of the metrics subsystem persisting |
222 // metrics as the application goes into the background. The application may be | 185 // metrics as the application goes into the background. The application may be |
223 // killed at any time after this method is invoked without further | 186 // killed at any time after this method is invoked without further |
224 // notification. | 187 // notification. |
225 void FlushMetricsOnAppEnterBackground(); | 188 void FlushMetricsOnAppEnterBackground(); |
226 | 189 |
227 void NotifyClientRedirectTo(const PageLoadTracker& destination); | 190 void NotifyClientRedirectTo(const PageLoadTracker& destination); |
228 | 191 |
229 void UpdateTiming(const mojom::PageLoadTiming& timing, | |
230 const mojom::PageLoadMetadata& metadata); | |
231 | |
232 void UpdateSubFrameTiming(content::RenderFrameHost* render_frame_host, | |
233 const mojom::PageLoadTiming& new_timing, | |
234 const mojom::PageLoadMetadata& new_metadata); | |
235 | |
236 // Update metadata for child frames. Updates for child frames arrive | |
237 // separately from updates for the main frame, so aren't included in | |
238 // UpdateTiming. | |
239 void UpdateSubFrameMetadata(const mojom::PageLoadMetadata& subframe_metadata); | |
240 | |
241 void OnStartedResource( | 192 void OnStartedResource( |
242 const ExtraRequestStartInfo& extra_request_started_info); | 193 const ExtraRequestStartInfo& extra_request_started_info); |
243 | 194 |
244 void OnLoadedResource( | 195 void OnLoadedResource( |
245 const ExtraRequestCompleteInfo& extra_request_complete_info); | 196 const ExtraRequestCompleteInfo& extra_request_complete_info); |
246 | 197 |
247 // Signals that we should stop tracking metrics for the associated page load. | 198 // Signals that we should stop tracking metrics for the associated page load. |
248 // We may stop tracking a page load if it doesn't meet the criteria for | 199 // We may stop tracking a page load if it doesn't meet the criteria for |
249 // tracking metrics in DidFinishNavigation. | 200 // tracking metrics in DidFinishNavigation. |
250 void StopTracking(); | 201 void StopTracking(); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
282 // and is simpler than other feasible methods. See https://goo.gl/WKRG98. | 233 // and is simpler than other feasible methods. See https://goo.gl/WKRG98. |
283 bool IsLikelyProvisionalAbort(base::TimeTicks abort_cause_time) const; | 234 bool IsLikelyProvisionalAbort(base::TimeTicks abort_cause_time) const; |
284 | 235 |
285 bool MatchesOriginalNavigation(content::NavigationHandle* navigation_handle); | 236 bool MatchesOriginalNavigation(content::NavigationHandle* navigation_handle); |
286 | 237 |
287 bool did_commit() const { return did_commit_; } | 238 bool did_commit() const { return did_commit_; } |
288 const GURL& url() const { return url_; } | 239 const GURL& url() const { return url_; } |
289 | 240 |
290 base::TimeTicks navigation_start() const { return navigation_start_; } | 241 base::TimeTicks navigation_start() const { return navigation_start_; } |
291 | 242 |
292 PageLoadExtraInfo ComputePageLoadExtraInfo(); | 243 PageLoadExtraInfo ComputePageLoadExtraInfo() const; |
293 | 244 |
294 ui::PageTransition page_transition() const { return page_transition_; } | 245 ui::PageTransition page_transition() const { return page_transition_; } |
295 | 246 |
296 UserInitiatedInfo user_initiated_info() const { return user_initiated_info_; } | 247 UserInitiatedInfo user_initiated_info() const { return user_initiated_info_; } |
297 | 248 |
298 UserInputTracker* input_tracker() { return &input_tracker_; } | 249 UserInputTracker* input_tracker() { return &input_tracker_; } |
299 | 250 |
| 251 PageLoadMetricsUpdateDispatcher* metrics_update_dispatcher() { |
| 252 return &metrics_update_dispatcher_; |
| 253 } |
| 254 |
300 // Whether this PageLoadTracker has a navigation GlobalRequestID that matches | 255 // Whether this PageLoadTracker has a navigation GlobalRequestID that matches |
301 // the given request_id. This method will return false before | 256 // the given request_id. This method will return false before |
302 // WillProcessNavigationResponse has been invoked, as PageLoadTracker doesn't | 257 // WillProcessNavigationResponse has been invoked, as PageLoadTracker doesn't |
303 // know its GlobalRequestID until WillProcessNavigationResponse has been | 258 // know its GlobalRequestID until WillProcessNavigationResponse has been |
304 // invoked. | 259 // invoked. |
305 bool HasMatchingNavigationRequestID( | 260 bool HasMatchingNavigationRequestID( |
306 const content::GlobalRequestID& request_id) const; | 261 const content::GlobalRequestID& request_id) const; |
307 | 262 |
308 // Invoked when a media element starts playing. | 263 // Invoked when a media element starts playing. |
309 void MediaStartedPlaying( | 264 void MediaStartedPlaying( |
310 const content::WebContentsObserver::MediaPlayerInfo& video_type, | 265 const content::WebContentsObserver::MediaPlayerInfo& video_type, |
311 bool is_in_main_frame); | 266 bool is_in_main_frame); |
312 | 267 |
313 // Invoked on navigations where a navigation delay was added by the | 268 // Invoked on navigations where a navigation delay was added by the |
314 // DelayNavigationThrottle. This is a temporary method that will be removed | 269 // DelayNavigationThrottle. This is a temporary method that will be removed |
315 // once the experiment is complete. | 270 // once the experiment is complete. |
316 void OnNavigationDelayComplete(base::TimeDelta scheduled_delay, | 271 void OnNavigationDelayComplete(base::TimeDelta scheduled_delay, |
317 base::TimeDelta actual_delay); | 272 base::TimeDelta actual_delay); |
318 | 273 |
319 private: | 274 private: |
320 using FrameTreeNodeId = int; | |
321 | |
322 // This function converts a TimeTicks value taken in the browser process | 275 // This function converts a TimeTicks value taken in the browser process |
323 // to navigation_start_ if: | 276 // to navigation_start_ if: |
324 // - base::TimeTicks is not comparable across processes because the clock | 277 // - base::TimeTicks is not comparable across processes because the clock |
325 // is not system wide monotonic. | 278 // is not system wide monotonic. |
326 // - *event_time < navigation_start_ | 279 // - *event_time < navigation_start_ |
327 void ClampBrowserTimestampIfInterProcessTimeTickSkew( | 280 void ClampBrowserTimestampIfInterProcessTimeTickSkew( |
328 base::TimeTicks* event_time); | 281 base::TimeTicks* event_time); |
329 | 282 |
330 void UpdatePageEndInternal(PageEndReason page_end_reason, | 283 void UpdatePageEndInternal(PageEndReason page_end_reason, |
331 UserInitiatedInfo user_initiated_info, | 284 UserInitiatedInfo user_initiated_info, |
332 base::TimeTicks timestamp, | 285 base::TimeTicks timestamp, |
333 bool is_certainly_browser_timestamp); | 286 bool is_certainly_browser_timestamp); |
334 | 287 |
335 // If |final_navigation| is null, then this is an "unparented" abort chain, | 288 // If |final_navigation| is null, then this is an "unparented" abort chain, |
336 // and represents a sequence of provisional aborts that never ends with a | 289 // and represents a sequence of provisional aborts that never ends with a |
337 // committed load. | 290 // committed load. |
338 void LogAbortChainHistograms(content::NavigationHandle* final_navigation); | 291 void LogAbortChainHistograms(content::NavigationHandle* final_navigation); |
339 | 292 |
340 void MaybeUpdateURL(content::NavigationHandle* navigation_handle); | 293 void MaybeUpdateURL(content::NavigationHandle* navigation_handle); |
341 | 294 |
342 // Merge values from |new_paint_timing| into |merged_page_timing_|, offsetting | |
343 // any new timings by the |navigation_start_offset|. | |
344 void MergePaintTiming(base::TimeDelta navigation_start_offset, | |
345 const mojom::PaintTiming& new_paint_timing, | |
346 bool is_main_frame); | |
347 | |
348 void DispatchTimingUpdates(); | |
349 | |
350 UserInputTracker input_tracker_; | 295 UserInputTracker input_tracker_; |
351 | 296 |
352 // Whether we stopped tracking this navigation after it was initiated. We may | 297 // Whether we stopped tracking this navigation after it was initiated. We may |
353 // stop tracking a navigation if it doesn't meet the criteria for tracking | 298 // stop tracking a navigation if it doesn't meet the criteria for tracking |
354 // metrics in DidFinishNavigation. | 299 // metrics in DidFinishNavigation. |
355 bool did_stop_tracking_; | 300 bool did_stop_tracking_; |
356 | 301 |
357 // Whether the application went into the background when this PageLoadTracker | 302 // Whether the application went into the background when this PageLoadTracker |
358 // was active. This is a temporary boolean for UMA tracking. | 303 // was active. This is a temporary boolean for UMA tracking. |
359 bool app_entered_background_; | 304 bool app_entered_background_; |
(...skipping 29 matching lines...) Expand all Loading... |
389 | 334 |
390 base::TimeTicks page_end_time_; | 335 base::TimeTicks page_end_time_; |
391 | 336 |
392 // We record separate metrics for events that occur after a background, | 337 // We record separate metrics for events that occur after a background, |
393 // because metrics like layout/paint are delayed artificially | 338 // because metrics like layout/paint are delayed artificially |
394 // when they occur in the background. | 339 // when they occur in the background. |
395 base::TimeTicks background_time_; | 340 base::TimeTicks background_time_; |
396 base::TimeTicks foreground_time_; | 341 base::TimeTicks foreground_time_; |
397 bool started_in_foreground_; | 342 bool started_in_foreground_; |
398 | 343 |
399 // PageLoadTiming for the currently tracked page. The fields in |paint_timing| | |
400 // are merged across all frames in the document. All other fields are for the | |
401 // main frame document. | |
402 mojom::PageLoadTimingPtr merged_page_timing_; | |
403 mojom::PageLoadTimingPtr last_dispatched_merged_page_timing_; | 344 mojom::PageLoadTimingPtr last_dispatched_merged_page_timing_; |
404 | 345 |
405 mojom::PageLoadMetadata main_frame_metadata_; | |
406 mojom::PageLoadMetadataPtr last_dispatched_main_frame_metadata_; | |
407 | |
408 mojom::PageLoadMetadata subframe_metadata_; | |
409 | |
410 ui::PageTransition page_transition_; | 346 ui::PageTransition page_transition_; |
411 | 347 |
412 base::Optional<content::GlobalRequestID> navigation_request_id_; | 348 base::Optional<content::GlobalRequestID> navigation_request_id_; |
413 | 349 |
414 // Whether this page load was user initiated. | 350 // Whether this page load was user initiated. |
415 UserInitiatedInfo user_initiated_info_; | 351 UserInitiatedInfo user_initiated_info_; |
416 | 352 |
417 // This is a subtle member. If a provisional load A gets aborted by | 353 // This is a subtle member. If a provisional load A gets aborted by |
418 // provisional load B, which gets aborted by C that eventually commits, then | 354 // provisional load B, which gets aborted by C that eventually commits, then |
419 // there exists an abort chain of length 2, starting at A's navigation_start. | 355 // there exists an abort chain of length 2, starting at A's navigation_start. |
420 // This is useful because it allows histograming abort chain lengths based on | 356 // This is useful because it allows histograming abort chain lengths based on |
421 // what the last load's transition type is. i.e. holding down F-5 to spam | 357 // what the last load's transition type is. i.e. holding down F-5 to spam |
422 // reload will produce a long chain with the RELOAD transition. | 358 // reload will produce a long chain with the RELOAD transition. |
423 const int aborted_chain_size_; | 359 const int aborted_chain_size_; |
424 | 360 |
425 // This member counts consecutive provisional aborts that share a url. It will | 361 // This member counts consecutive provisional aborts that share a url. It will |
426 // always be less than or equal to |aborted_chain_size_|. | 362 // always be less than or equal to |aborted_chain_size_|. |
427 const int aborted_chain_size_same_url_; | 363 const int aborted_chain_size_same_url_; |
428 | 364 |
429 // Interface to chrome features. Must outlive the class. | 365 // Interface to chrome features. Must outlive the class. |
430 PageLoadMetricsEmbedderInterface* const embedder_interface_; | 366 PageLoadMetricsEmbedderInterface* const embedder_interface_; |
431 | 367 |
432 std::vector<std::unique_ptr<PageLoadMetricsObserver>> observers_; | 368 std::vector<std::unique_ptr<PageLoadMetricsObserver>> observers_; |
433 | 369 |
434 // Navigation start offsets for the most recently committed document in each | 370 PageLoadMetricsUpdateDispatcher metrics_update_dispatcher_; |
435 // frame. | |
436 std::map<FrameTreeNodeId, base::TimeDelta> subframe_navigation_start_offset_; | |
437 | 371 |
438 DISALLOW_COPY_AND_ASSIGN(PageLoadTracker); | 372 DISALLOW_COPY_AND_ASSIGN(PageLoadTracker); |
439 }; | 373 }; |
440 | 374 |
441 } // namespace page_load_metrics | 375 } // namespace page_load_metrics |
442 | 376 |
443 #endif // CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_TRACKER_H_ | 377 #endif // CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_TRACKER_H_ |
OLD | NEW |