| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 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/metrics_web_contents_observer.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> |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 58 const char kAbortChainSizeNewNavigation[] = | 58 const char kAbortChainSizeNewNavigation[] = |
| 59 "PageLoad.Internal.ProvisionalAbortChainSize.NewNavigation"; | 59 "PageLoad.Internal.ProvisionalAbortChainSize.NewNavigation"; |
| 60 const char kAbortChainSizeSameURL[] = | 60 const char kAbortChainSizeSameURL[] = |
| 61 "PageLoad.Internal.ProvisionalAbortChainSize.SameURL"; | 61 "PageLoad.Internal.ProvisionalAbortChainSize.SameURL"; |
| 62 const char kAbortChainSizeNoCommit[] = | 62 const char kAbortChainSizeNoCommit[] = |
| 63 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; | 63 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; |
| 64 const char kClientRedirectFirstPaintToNavigation[] = | 64 const char kClientRedirectFirstPaintToNavigation[] = |
| 65 "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation"; | 65 "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation"; |
| 66 const char kClientRedirectWithoutPaint[] = | 66 const char kClientRedirectWithoutPaint[] = |
| 67 "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"; | 67 "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"; |
| 68 const char kCommitToCompleteNoTimingIPCs[] = | |
| 69 "PageLoad.Internal.CommitToComplete.NoTimingIPCs"; | |
| 70 const char kPageLoadCompletedAfterAppBackground[] = | 68 const char kPageLoadCompletedAfterAppBackground[] = |
| 71 "PageLoad.Internal.PageLoadCompleted.AfterAppBackground"; | 69 "PageLoad.Internal.PageLoadCompleted.AfterAppBackground"; |
| 72 | 70 |
| 73 } // namespace internal | 71 } // namespace internal |
| 74 | 72 |
| 75 namespace { | 73 namespace { |
| 76 | 74 |
| 77 // Helper to allow use of Optional<> values in LOG() messages. | 75 // Helper to allow use of Optional<> values in LOG() messages. |
| 78 std::ostream& operator<<(std::ostream& os, | 76 std::ostream& operator<<(std::ostream& os, |
| 79 const base::Optional<base::TimeDelta>& opt) { | 77 const base::Optional<base::TimeDelta>& opt) { |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 295 PageLoadTracker::PageLoadTracker( | 293 PageLoadTracker::PageLoadTracker( |
| 296 bool in_foreground, | 294 bool in_foreground, |
| 297 PageLoadMetricsEmbedderInterface* embedder_interface, | 295 PageLoadMetricsEmbedderInterface* embedder_interface, |
| 298 const GURL& currently_committed_url, | 296 const GURL& currently_committed_url, |
| 299 content::NavigationHandle* navigation_handle, | 297 content::NavigationHandle* navigation_handle, |
| 300 int aborted_chain_size, | 298 int aborted_chain_size, |
| 301 int aborted_chain_size_same_url) | 299 int aborted_chain_size_same_url) |
| 302 : did_stop_tracking_(false), | 300 : did_stop_tracking_(false), |
| 303 app_entered_background_(false), | 301 app_entered_background_(false), |
| 304 navigation_start_(navigation_handle->NavigationStart()), | 302 navigation_start_(navigation_handle->NavigationStart()), |
| 305 url_(navigation_handle->GetURL()), | 303 start_url_(navigation_handle->GetURL()), |
| 306 start_url_(url_), | |
| 307 abort_type_(ABORT_NONE), | 304 abort_type_(ABORT_NONE), |
| 308 abort_user_initiated_(false), | 305 abort_user_initiated_(false), |
| 309 started_in_foreground_(in_foreground), | 306 started_in_foreground_(in_foreground), |
| 310 page_transition_(navigation_handle->GetPageTransition()), | 307 page_transition_(navigation_handle->GetPageTransition()), |
| 311 num_cache_requests_(0), | 308 num_cache_requests_(0), |
| 312 num_network_requests_(0), | 309 num_network_requests_(0), |
| 313 user_gesture_(IsNavigationUserInitiated(navigation_handle)), | 310 user_gesture_(IsNavigationUserInitiated(navigation_handle)), |
| 314 aborted_chain_size_(aborted_chain_size), | 311 aborted_chain_size_(aborted_chain_size), |
| 315 aborted_chain_size_same_url_(aborted_chain_size_same_url), | 312 aborted_chain_size_same_url_(aborted_chain_size_same_url), |
| 316 embedder_interface_(embedder_interface) { | 313 embedder_interface_(embedder_interface) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 330 } | 327 } |
| 331 | 328 |
| 332 PageLoadTracker::~PageLoadTracker() { | 329 PageLoadTracker::~PageLoadTracker() { |
| 333 if (app_entered_background_) { | 330 if (app_entered_background_) { |
| 334 RecordAppBackgroundPageLoadCompleted(true); | 331 RecordAppBackgroundPageLoadCompleted(true); |
| 335 } | 332 } |
| 336 | 333 |
| 337 if (did_stop_tracking_) | 334 if (did_stop_tracking_) |
| 338 return; | 335 return; |
| 339 | 336 |
| 340 if (commit_time_.is_null()) { | 337 if (committed_url_.is_empty()) { |
| 341 if (!failed_provisional_load_info_) | 338 if (!failed_provisional_load_info_) |
| 342 RecordInternalError(ERR_NO_COMMIT_OR_FAILED_PROVISIONAL_LOAD); | 339 RecordInternalError(ERR_NO_COMMIT_OR_FAILED_PROVISIONAL_LOAD); |
| 343 | 340 |
| 344 // Don't include any aborts that resulted in a new navigation, as the chain | 341 // Don't include any aborts that resulted in a new navigation, as the chain |
| 345 // length will be included in the aborter PageLoadTracker. | 342 // length will be included in the aborter PageLoadTracker. |
| 346 if (abort_type_ != ABORT_RELOAD && abort_type_ != ABORT_FORWARD_BACK && | 343 if (abort_type_ != ABORT_RELOAD && abort_type_ != ABORT_FORWARD_BACK && |
| 347 abort_type_ != ABORT_NEW_NAVIGATION) { | 344 abort_type_ != ABORT_NEW_NAVIGATION) { |
| 348 LogAbortChainHistograms(nullptr); | 345 LogAbortChainHistograms(nullptr); |
| 349 } | 346 } |
| 350 } else if (timing_.IsEmpty()) { | 347 } else if (timing_.IsEmpty()) { |
| 351 RecordInternalError(ERR_NO_IPCS_RECEIVED); | 348 RecordInternalError(ERR_NO_IPCS_RECEIVED); |
| 352 PAGE_LOAD_HISTOGRAM(internal::kCommitToCompleteNoTimingIPCs, | |
| 353 base::TimeTicks::Now() - commit_time_); | |
| 354 } | 349 } |
| 355 | 350 |
| 356 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); | 351 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
| 357 for (const auto& observer : observers_) { | 352 for (const auto& observer : observers_) { |
| 358 if (failed_provisional_load_info_) { | 353 if (failed_provisional_load_info_) { |
| 359 observer->OnFailedProvisionalLoad(*failed_provisional_load_info_, info); | 354 observer->OnFailedProvisionalLoad(*failed_provisional_load_info_, info); |
| 360 } else if (info.time_to_commit) { | 355 } else if (!info.committed_url.is_empty()) { |
| 361 observer->OnComplete(timing_, info); | 356 observer->OnComplete(timing_, info); |
| 362 } | 357 } |
| 363 } | 358 } |
| 364 } | 359 } |
| 365 | 360 |
| 366 void PageLoadTracker::LogAbortChainHistograms( | 361 void PageLoadTracker::LogAbortChainHistograms( |
| 367 content::NavigationHandle* final_navigation) { | 362 content::NavigationHandle* final_navigation) { |
| 368 if (aborted_chain_size_ == 0) | 363 if (aborted_chain_size_ == 0) |
| 369 return; | 364 return; |
| 370 // Note that this could be broken out by this navigation's abort type, if more | 365 // Note that this could be broken out by this navigation's abort type, if more |
| 371 // granularity is needed. Add one to the chain size to count the current | 366 // granularity is needed. Add one to the chain size to count the current |
| 372 // navigation. In the other cases, the current navigation is the final | 367 // navigation. In the other cases, the current navigation is the final |
| 373 // navigation (which commits). | 368 // navigation (which commits). |
| 374 if (!final_navigation) { | 369 if (!final_navigation) { |
| 375 UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeNoCommit, | 370 UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeNoCommit, |
| 376 aborted_chain_size_ + 1); | 371 aborted_chain_size_ + 1); |
| 377 LogAbortChainSameURLHistogram(aborted_chain_size_same_url_ + 1); | 372 LogAbortChainSameURLHistogram(aborted_chain_size_same_url_ + 1); |
| 378 return; | 373 return; |
| 379 } | 374 } |
| 380 | 375 |
| 381 // The following is only executed for committing trackers. | 376 // The following is only executed for committing trackers. |
| 382 DCHECK(!commit_time_.is_null()); | 377 DCHECK(!committed_url_.is_empty()); |
| 383 | 378 |
| 384 // Note that histograms could be separated out by this commit's transition | 379 // Note that histograms could be separated out by this commit's transition |
| 385 // type, but for simplicity they will all be bucketed together. | 380 // type, but for simplicity they will all be bucketed together. |
| 386 LogAbortChainSameURLHistogram(aborted_chain_size_same_url_); | 381 LogAbortChainSameURLHistogram(aborted_chain_size_same_url_); |
| 387 | 382 |
| 388 ui::PageTransition committed_transition = | 383 ui::PageTransition committed_transition = |
| 389 final_navigation->GetPageTransition(); | 384 final_navigation->GetPageTransition(); |
| 390 switch (AbortTypeForPageTransition(committed_transition)) { | 385 switch (AbortTypeForPageTransition(committed_transition)) { |
| 391 case ABORT_RELOAD: | 386 case ABORT_RELOAD: |
| 392 UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeReload, | 387 UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeReload, |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 445 DCHECK_NE(started_in_foreground_, background_time_.is_null()); | 440 DCHECK_NE(started_in_foreground_, background_time_.is_null()); |
| 446 foreground_time_ = base::TimeTicks::Now(); | 441 foreground_time_ = base::TimeTicks::Now(); |
| 447 ClampBrowserTimestampIfInterProcessTimeTickSkew(&foreground_time_); | 442 ClampBrowserTimestampIfInterProcessTimeTickSkew(&foreground_time_); |
| 448 } | 443 } |
| 449 | 444 |
| 450 for (const auto& observer : observers_) | 445 for (const auto& observer : observers_) |
| 451 observer->OnShown(); | 446 observer->OnShown(); |
| 452 } | 447 } |
| 453 | 448 |
| 454 void PageLoadTracker::Commit(content::NavigationHandle* navigation_handle) { | 449 void PageLoadTracker::Commit(content::NavigationHandle* navigation_handle) { |
| 455 // TODO(bmcquade): To improve accuracy, consider adding commit time to | 450 committed_url_ = navigation_handle->GetURL(); |
| 456 // NavigationHandle. Taking a timestamp here should be close enough for now. | |
| 457 commit_time_ = base::TimeTicks::Now(); | |
| 458 ClampBrowserTimestampIfInterProcessTimeTickSkew(&commit_time_); | |
| 459 url_ = navigation_handle->GetURL(); | |
| 460 // Some transitions (like CLIENT_REDIRECT) are only known at commit time. | 451 // Some transitions (like CLIENT_REDIRECT) are only known at commit time. |
| 461 page_transition_ = navigation_handle->GetPageTransition(); | 452 page_transition_ = navigation_handle->GetPageTransition(); |
| 462 user_gesture_ = navigation_handle->HasUserGesture(); | 453 user_gesture_ = navigation_handle->HasUserGesture(); |
| 463 | 454 |
| 464 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnCommit, navigation_handle); | 455 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnCommit, navigation_handle); |
| 465 LogAbortChainHistograms(navigation_handle); | 456 LogAbortChainHistograms(navigation_handle); |
| 466 } | 457 } |
| 467 | 458 |
| 468 void PageLoadTracker::FailedProvisionalLoad( | 459 void PageLoadTracker::FailedProvisionalLoad( |
| 469 content::NavigationHandle* navigation_handle) { | 460 content::NavigationHandle* navigation_handle) { |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 520 // different start time from an earlier struct is considered invalid. | 511 // different start time from an earlier struct is considered invalid. |
| 521 bool valid_timing_descendent = | 512 bool valid_timing_descendent = |
| 522 timing_.navigation_start.is_null() || | 513 timing_.navigation_start.is_null() || |
| 523 timing_.navigation_start == new_timing.navigation_start; | 514 timing_.navigation_start == new_timing.navigation_start; |
| 524 // Ensure flags sent previously are still present in the new metadata fields. | 515 // Ensure flags sent previously are still present in the new metadata fields. |
| 525 bool valid_behavior_descendent = | 516 bool valid_behavior_descendent = |
| 526 (metadata_.behavior_flags & new_metadata.behavior_flags) == | 517 (metadata_.behavior_flags & new_metadata.behavior_flags) == |
| 527 metadata_.behavior_flags; | 518 metadata_.behavior_flags; |
| 528 if (IsValidPageLoadTiming(new_timing) && valid_timing_descendent && | 519 if (IsValidPageLoadTiming(new_timing) && valid_timing_descendent && |
| 529 valid_behavior_descendent) { | 520 valid_behavior_descendent) { |
| 530 DCHECK(!commit_time_.is_null()); // OnCommit() must be called first. | 521 DCHECK(!committed_url_.is_empty()); // OnCommit() must be called first. |
| 531 // There are some subtle ordering constraints here. GetPageLoadMetricsInfo() | 522 // There are some subtle ordering constraints here. GetPageLoadMetricsInfo() |
| 532 // must be called before DispatchObserverTimingCallbacks, but its | 523 // must be called before DispatchObserverTimingCallbacks, but its |
| 533 // implementation depends on the state of metadata_, so we need to update | 524 // implementation depends on the state of metadata_, so we need to update |
| 534 // metadata_ before calling GetPageLoadMetricsInfo. Thus, we make a copy of | 525 // metadata_ before calling GetPageLoadMetricsInfo. Thus, we make a copy of |
| 535 // timing here, update timing_ and metadata_, and then proceed to dispatch | 526 // timing here, update timing_ and metadata_, and then proceed to dispatch |
| 536 // the observer timing callbacks. | 527 // the observer timing callbacks. |
| 537 const PageLoadTiming last_timing = timing_; | 528 const PageLoadTiming last_timing = timing_; |
| 538 timing_ = new_timing; | 529 timing_ = new_timing; |
| 539 | 530 |
| 540 const PageLoadMetadata last_metadata = metadata_; | 531 const PageLoadMetadata last_metadata = metadata_; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 591 if (!event_time->is_null() && *event_time < navigation_start_) { | 582 if (!event_time->is_null() && *event_time < navigation_start_) { |
| 592 RecordInternalError(ERR_INTER_PROCESS_TIME_TICK_SKEW); | 583 RecordInternalError(ERR_INTER_PROCESS_TIME_TICK_SKEW); |
| 593 *event_time = navigation_start_; | 584 *event_time = navigation_start_; |
| 594 } | 585 } |
| 595 } | 586 } |
| 596 | 587 |
| 597 PageLoadExtraInfo PageLoadTracker::ComputePageLoadExtraInfo() { | 588 PageLoadExtraInfo PageLoadTracker::ComputePageLoadExtraInfo() { |
| 598 base::Optional<base::TimeDelta> first_background_time; | 589 base::Optional<base::TimeDelta> first_background_time; |
| 599 base::Optional<base::TimeDelta> first_foreground_time; | 590 base::Optional<base::TimeDelta> first_foreground_time; |
| 600 base::Optional<base::TimeDelta> time_to_abort; | 591 base::Optional<base::TimeDelta> time_to_abort; |
| 601 base::Optional<base::TimeDelta> time_to_commit; | |
| 602 | 592 |
| 603 if (!background_time_.is_null()) { | 593 if (!background_time_.is_null()) { |
| 604 DCHECK_GE(background_time_, navigation_start_); | 594 DCHECK_GE(background_time_, navigation_start_); |
| 605 first_background_time = background_time_ - navigation_start_; | 595 first_background_time = background_time_ - navigation_start_; |
| 606 } | 596 } |
| 607 | 597 |
| 608 if (!foreground_time_.is_null()) { | 598 if (!foreground_time_.is_null()) { |
| 609 DCHECK_GE(foreground_time_, navigation_start_); | 599 DCHECK_GE(foreground_time_, navigation_start_); |
| 610 first_foreground_time = foreground_time_ - navigation_start_; | 600 first_foreground_time = foreground_time_ - navigation_start_; |
| 611 } | 601 } |
| 612 | 602 |
| 613 if (abort_type_ != ABORT_NONE) { | 603 if (abort_type_ != ABORT_NONE) { |
| 614 DCHECK_GE(abort_time_, navigation_start_); | 604 DCHECK_GE(abort_time_, navigation_start_); |
| 615 time_to_abort = abort_time_ - navigation_start_; | 605 time_to_abort = abort_time_ - navigation_start_; |
| 616 } else { | 606 } else { |
| 617 DCHECK(abort_time_.is_null()); | 607 DCHECK(abort_time_.is_null()); |
| 618 } | 608 } |
| 619 | 609 |
| 620 if (!commit_time_.is_null()) { | |
| 621 DCHECK_GE(commit_time_, navigation_start_); | |
| 622 time_to_commit = commit_time_ - navigation_start_; | |
| 623 } | |
| 624 | |
| 625 // abort_type_ == ABORT_NONE implies !abort_user_initiated_. | 610 // abort_type_ == ABORT_NONE implies !abort_user_initiated_. |
| 626 DCHECK(abort_type_ != ABORT_NONE || !abort_user_initiated_); | 611 DCHECK(abort_type_ != ABORT_NONE || !abort_user_initiated_); |
| 627 return PageLoadExtraInfo( | 612 return PageLoadExtraInfo( |
| 628 first_background_time, first_foreground_time, started_in_foreground_, | 613 first_background_time, first_foreground_time, started_in_foreground_, |
| 629 user_gesture_, commit_time_.is_null() ? GURL() : url_, start_url_, | 614 user_gesture_, committed_url_, start_url_, abort_type_, |
| 630 time_to_commit, abort_type_, abort_user_initiated_, time_to_abort, | 615 abort_user_initiated_, time_to_abort, num_cache_requests_, |
| 631 num_cache_requests_, num_network_requests_, metadata_); | 616 num_network_requests_, metadata_); |
| 632 } | 617 } |
| 633 | 618 |
| 634 void PageLoadTracker::NotifyAbort(UserAbortType abort_type, | 619 void PageLoadTracker::NotifyAbort(UserAbortType abort_type, |
| 635 bool user_initiated, | 620 bool user_initiated, |
| 636 base::TimeTicks timestamp, | 621 base::TimeTicks timestamp, |
| 637 bool is_certainly_browser_timestamp) { | 622 bool is_certainly_browser_timestamp) { |
| 638 DCHECK_NE(abort_type, ABORT_NONE); | 623 DCHECK_NE(abort_type, ABORT_NONE); |
| 639 // Use UpdateAbort to update an already notified PageLoadTracker. | 624 // Use UpdateAbort to update an already notified PageLoadTracker. |
| 640 if (abort_type_ != ABORT_NONE) | 625 if (abort_type_ != ABORT_NONE) |
| 641 return; | 626 return; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 664 base::TimeTicks abort_cause_time) const { | 649 base::TimeTicks abort_cause_time) const { |
| 665 // Note that |abort_cause_time - abort_time| can be negative. | 650 // Note that |abort_cause_time - abort_time| can be negative. |
| 666 return abort_type_ == ABORT_OTHER && | 651 return abort_type_ == ABORT_OTHER && |
| 667 (abort_cause_time - abort_time_).InMilliseconds() < 100; | 652 (abort_cause_time - abort_time_).InMilliseconds() < 100; |
| 668 } | 653 } |
| 669 | 654 |
| 670 bool PageLoadTracker::MatchesOriginalNavigation( | 655 bool PageLoadTracker::MatchesOriginalNavigation( |
| 671 content::NavigationHandle* navigation_handle) { | 656 content::NavigationHandle* navigation_handle) { |
| 672 // Neither navigation should have committed. | 657 // Neither navigation should have committed. |
| 673 DCHECK(!navigation_handle->HasCommitted()); | 658 DCHECK(!navigation_handle->HasCommitted()); |
| 674 DCHECK(commit_time_.is_null()); | 659 DCHECK(committed_url_.is_empty()); |
| 675 return navigation_handle->GetURL() == url_; | 660 return navigation_handle->GetURL() == start_url_; |
| 676 } | 661 } |
| 677 | 662 |
| 678 void PageLoadTracker::UpdateAbortInternal(UserAbortType abort_type, | 663 void PageLoadTracker::UpdateAbortInternal(UserAbortType abort_type, |
| 679 bool user_initiated, | 664 bool user_initiated, |
| 680 base::TimeTicks timestamp, | 665 base::TimeTicks timestamp, |
| 681 bool is_certainly_browser_timestamp) { | 666 bool is_certainly_browser_timestamp) { |
| 682 // When a provisional navigation commits, that navigation's start time is | 667 // When a provisional navigation commits, that navigation's start time is |
| 683 // interpreted as the abort time for other provisional loads in the tab. | 668 // interpreted as the abort time for other provisional loads in the tab. |
| 684 // However, this only makes sense if the committed load started after the | 669 // However, this only makes sense if the committed load started after the |
| 685 // aborted provisional loads started. Thus we ignore cases where the committed | 670 // aborted provisional loads started. Thus we ignore cases where the committed |
| (...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1105 content::NavigationHandle* navigation_handle) const { | 1090 content::NavigationHandle* navigation_handle) const { |
| 1106 DCHECK(navigation_handle->IsInMainFrame()); | 1091 DCHECK(navigation_handle->IsInMainFrame()); |
| 1107 DCHECK(!navigation_handle->HasCommitted() || | 1092 DCHECK(!navigation_handle->HasCommitted() || |
| 1108 !navigation_handle->IsSamePage()); | 1093 !navigation_handle->IsSamePage()); |
| 1109 | 1094 |
| 1110 return BrowserPageTrackDecider(embedder_interface_.get(), web_contents(), | 1095 return BrowserPageTrackDecider(embedder_interface_.get(), web_contents(), |
| 1111 navigation_handle).ShouldTrack(); | 1096 navigation_handle).ShouldTrack(); |
| 1112 } | 1097 } |
| 1113 | 1098 |
| 1114 } // namespace page_load_metrics | 1099 } // namespace page_load_metrics |
| OLD | NEW |