summaryrefslogtreecommitdiff
path: root/chromium/components/page_load_metrics
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/page_load_metrics')
-rw-r--r--chromium/components/page_load_metrics/browser/BUILD.gn3
-rw-r--r--chromium/components/page_load_metrics/browser/metrics_web_contents_observer.cc112
-rw-r--r--chromium/components/page_load_metrics/browser/metrics_web_contents_observer.h19
-rw-r--r--chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.cc60
-rw-r--r--chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h38
-rw-r--r--chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc253
-rw-r--r--chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h44
-rw-r--r--chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc448
-rw-r--r--chromium/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc117
-rw-r--r--chromium/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h83
-rw-r--r--chromium/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc4
-rw-r--r--chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h1
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc3
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_observer.cc29
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_observer.h33
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.cc16
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h27
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc38
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.h7
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc71
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h12
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_util.cc13
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_util.h5
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_tracker.cc158
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_tracker.h29
-rw-r--r--chromium/components/page_load_metrics/common/page_end_reason.h8
-rw-r--r--chromium/components/page_load_metrics/common/page_load_metrics.mojom88
-rw-r--r--chromium/components/page_load_metrics/common/page_load_timing.cc26
-rw-r--r--chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.cc182
-rw-r--r--chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.h16
-rw-r--r--chromium/components/page_load_metrics/renderer/metrics_render_frame_observer_unittest.cc13
-rw-r--r--chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.cc4
32 files changed, 1482 insertions, 478 deletions
diff --git a/chromium/components/page_load_metrics/browser/BUILD.gn b/chromium/components/page_load_metrics/browser/BUILD.gn
index 8383f36b9af..c27d2e44573 100644
--- a/chromium/components/page_load_metrics/browser/BUILD.gn
+++ b/chromium/components/page_load_metrics/browser/BUILD.gn
@@ -10,6 +10,8 @@ source_set("browser") {
"metrics_navigation_throttle.h",
"metrics_web_contents_observer.cc",
"metrics_web_contents_observer.h",
+ "observers/back_forward_cache_page_load_metrics_observer.cc",
+ "observers/back_forward_cache_page_load_metrics_observer.h",
"observers/click_input_tracker.cc",
"observers/click_input_tracker.h",
"observers/core_page_load_metrics_observer.cc",
@@ -26,6 +28,7 @@ source_set("browser") {
"page_load_metrics_embedder_interface.h",
"page_load_metrics_observer.cc",
"page_load_metrics_observer.h",
+ "page_load_metrics_observer_delegate.cc",
"page_load_metrics_observer_delegate.h",
"page_load_metrics_update_dispatcher.cc",
"page_load_metrics_update_dispatcher.h",
diff --git a/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.cc b/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.cc
index c718da71648..2bade0dc409 100644
--- a/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.cc
+++ b/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.cc
@@ -76,23 +76,6 @@ void MetricsWebContentsObserver::RecordFeatureUsage(
observer->OnBrowserFeatureUsage(render_frame_host, new_features);
}
-MetricsWebContentsObserver::MetricsWebContentsObserver(
- content::WebContents* web_contents,
- std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface)
- : content::WebContentsObserver(web_contents),
- in_foreground_(web_contents->GetVisibility() !=
- content::Visibility::HIDDEN),
- embedder_interface_(std::move(embedder_interface)),
- has_navigated_(false),
- page_load_metrics_receiver_(web_contents, this) {
- // Prerenders erroneously report that they are initially visible, so we
- // manually override visibility state for prerender.
- if (embedder_interface_->IsPrerender(web_contents))
- in_foreground_ = false;
-
- RegisterInputEventObserver(web_contents->GetRenderViewHost());
-}
-
// static
MetricsWebContentsObserver* MetricsWebContentsObserver::CreateForWebContents(
content::WebContents* web_contents,
@@ -188,6 +171,23 @@ void MetricsWebContentsObserver::WillStartNavigationRequest(
has_navigated_ = true;
}
+MetricsWebContentsObserver::MetricsWebContentsObserver(
+ content::WebContents* web_contents,
+ std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface)
+ : content::WebContentsObserver(web_contents),
+ in_foreground_(web_contents->GetVisibility() !=
+ content::Visibility::HIDDEN),
+ embedder_interface_(std::move(embedder_interface)),
+ has_navigated_(false),
+ page_load_metrics_receiver_(web_contents, this) {
+ // Prerenders erroneously report that they are initially visible, so we
+ // manually override visibility state for prerender.
+ if (embedder_interface_->IsPrerender(web_contents))
+ in_foreground_ = false;
+
+ RegisterInputEventObserver(web_contents->GetRenderViewHost());
+}
+
void MetricsWebContentsObserver::WillStartNavigationRequestImpl(
content::NavigationHandle* navigation_handle) {
UserInitiatedInfo user_initiated_info(
@@ -397,6 +397,22 @@ void MetricsWebContentsObserver::OnCookiesAccessedImpl(
}
}
+void MetricsWebContentsObserver::DidActivatePortal(
+ content::WebContents* predecessor_web_contents,
+ base::TimeTicks activation_time) {
+ // The |predecessor_web_contents| is the WebContents that instantiated the
+ // portal.
+ MetricsWebContentsObserver* predecessor_observer =
+ MetricsWebContentsObserver::FromWebContents(predecessor_web_contents);
+ // We only track the portal activation if the predecessor is also being
+ // tracked.
+ if (!committed_load_ || !predecessor_observer ||
+ !predecessor_observer->committed_load_) {
+ return;
+ }
+ committed_load_->DidActivatePortal(activation_time);
+}
+
void MetricsWebContentsObserver::OnStorageAccessed(const GURL& url,
const GURL& first_party_url,
bool blocked_by_policy,
@@ -574,7 +590,11 @@ bool MetricsWebContentsObserver::MaybeRestorePageLoadTrackerForBackForwardCache(
committed_load_ = std::move(it->second);
back_forward_cached_pages_.erase(it);
- committed_load_->OnRestoreFromBackForwardCache();
+ committed_load_->OnRestoreFromBackForwardCache(navigation_handle);
+
+ for (auto& observer : testing_observers_)
+ observer.OnRestoredFromBackForwardCache(committed_load_.get());
+
return true;
}
@@ -783,24 +803,7 @@ void MetricsWebContentsObserver::OnTimingUpdated(
const bool is_main_frame = (render_frame_host->GetParent() == nullptr);
if (is_main_frame) {
- // While timings arriving for the wrong frame are expected, we do not expect
- // any of the errors below for main frames. Thus, we track occurrences of
- // all errors below, rather than returning early after encountering an
- // error.
- // TODO(crbug/1061090): Update page load metrics IPC validation to ues
- // mojo::ReportBadMessage.
- bool error = false;
- if (!committed_load_) {
- RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD);
- error = true;
- }
-
- if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
- RecordInternalError(ERR_IPC_FROM_BAD_URL_SCHEME);
- error = true;
- }
-
- if (error)
+ if (DoesTimingUpdateHaveError())
return;
} else if (!committed_load_) {
RecordInternalError(ERR_SUBFRAME_IPC_WITH_NO_RELEVANT_LOAD);
@@ -815,6 +818,26 @@ void MetricsWebContentsObserver::OnTimingUpdated(
}
}
+bool MetricsWebContentsObserver::DoesTimingUpdateHaveError() {
+ // While timings arriving for the wrong frame are expected, we do not expect
+ // any of the errors below for main frames. Thus, we track occurrences of
+ // all errors below, rather than returning early after encountering an
+ // error.
+ // TODO(crbug/1061090): Update page load metrics IPC validation to ues
+ // mojo::ReportBadMessage.
+ bool error = false;
+ if (!committed_load_) {
+ RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD);
+ error = true;
+ }
+
+ if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
+ RecordInternalError(ERR_IPC_FROM_BAD_URL_SCHEME);
+ error = true;
+ }
+ return error;
+}
+
void MetricsWebContentsObserver::UpdateTiming(
mojom::PageLoadTimingPtr timing,
mojom::FrameMetadataPtr metadata,
@@ -832,17 +855,22 @@ void MetricsWebContentsObserver::UpdateTiming(
std::move(input_timing_delta));
}
+void MetricsWebContentsObserver::SubmitThroughputData(
+ mojom::ThroughputUkmDataPtr throughput_data) {
+ if (DoesTimingUpdateHaveError())
+ return;
+
+ content::RenderFrameHost* render_frame_host =
+ page_load_metrics_receiver_.GetCurrentTargetFrame();
+ committed_load_->metrics_update_dispatcher()->UpdateThroughput(
+ render_frame_host, std::move(throughput_data));
+}
+
bool MetricsWebContentsObserver::ShouldTrackMainFrameNavigation(
content::NavigationHandle* navigation_handle) const {
DCHECK(navigation_handle->IsInMainFrame());
DCHECK(!navigation_handle->HasCommitted() ||
!navigation_handle->IsSameDocument());
- // If there is an outer WebContents, then this WebContents is embedded into
- // another one (it is either a portal or a Chrome App <webview>). Ignore these
- // navigations for now.
- if (web_contents()->GetOuterWebContents())
- return false;
-
// Ignore non-HTTP schemes (e.g. chrome://).
if (!navigation_handle->GetURL().SchemeIsHTTPOrHTTPS())
return false;
diff --git a/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.h b/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.h
index e511b3f562f..3fedb707723 100644
--- a/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.h
+++ b/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.h
@@ -65,6 +65,8 @@ class MetricsWebContentsObserver
// fine.
virtual void OnCommit(PageLoadTracker* tracker) {}
+ virtual void OnRestoredFromBackForwardCache(PageLoadTracker* tracker) {}
+
// Returns the observer delegate for the committed load associated with
// the MetricsWebContentsObserver.
const PageLoadMetricsObserverDelegate& GetDelegateForCommittedLoad();
@@ -85,9 +87,6 @@ class MetricsWebContentsObserver
static MetricsWebContentsObserver* CreateForWebContents(
content::WebContents* web_contents,
std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface);
- MetricsWebContentsObserver(
- content::WebContents* web_contents,
- std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface);
~MetricsWebContentsObserver() override;
// Any visibility changes that occur after this method should be ignored since
@@ -131,6 +130,8 @@ class MetricsWebContentsObserver
const GURL& first_party_url,
bool blocked_by_policy,
StorageType storage_type);
+ void DidActivatePortal(content::WebContents* predecessor_web_contents,
+ base::TimeTicks activation_time) override;
// These methods are forwarded from the MetricsNavigationThrottle.
void WillStartNavigationRequest(content::NavigationHandle* navigation_handle);
@@ -171,6 +172,10 @@ class MetricsWebContentsObserver
private:
friend class content::WebContentsUserData<MetricsWebContentsObserver>;
+ MetricsWebContentsObserver(
+ content::WebContents* web_contents,
+ std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface);
+
void WillStartNavigationRequestImpl(
content::NavigationHandle* navigation_handle);
@@ -184,6 +189,14 @@ class MetricsWebContentsObserver
mojom::DeferredResourceCountsPtr new_deferred_resource_data,
mojom::InputTimingPtr input_timing) override;
+ // Update the throughput samples on the browser side, and report it to UKM at
+ // page shuts down / nagivates away, report it to UKM.
+ void SubmitThroughputData(
+ mojom::ThroughputUkmDataPtr throughput_data) override;
+
+ // Common part for UpdateThroughput and OnTimingUpdated.
+ bool DoesTimingUpdateHaveError();
+
void HandleFailedNavigationForTrackedLoad(
content::NavigationHandle* navigation_handle,
std::unique_ptr<PageLoadTracker> tracker);
diff --git a/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.cc
new file mode 100644
index 00000000000..f055e06017a
--- /dev/null
+++ b/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.cc
@@ -0,0 +1,60 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h"
+
+#include "components/page_load_metrics/browser/page_load_metrics_util.h"
+
+namespace internal {
+
+const char kHistogramFirstPaintAfterBackForwardCacheRestore[] =
+ "PageLoad.PaintTiming.NavigationToFirstPaint.AfterBackForwardCacheRestore";
+const char kHistogramFirstInputDelayAfterBackForwardCacheRestore[] =
+ "PageLoad.InteractiveTiming.FirstInputDelay.AfterBackForwardCacheRestore";
+
+} // namespace internal
+
+BackForwardCachePageLoadMetricsObserver::
+ BackForwardCachePageLoadMetricsObserver() = default;
+
+BackForwardCachePageLoadMetricsObserver::
+ ~BackForwardCachePageLoadMetricsObserver() = default;
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+BackForwardCachePageLoadMetricsObserver::OnEnterBackForwardCache(
+ const page_load_metrics::mojom::PageLoadTiming& timing) {
+ return CONTINUE_OBSERVING;
+}
+
+void BackForwardCachePageLoadMetricsObserver::
+ OnFirstPaintAfterBackForwardCacheRestoreInPage(
+ const page_load_metrics::mojom::BackForwardCacheTiming& timing,
+ size_t index) {
+ auto first_paint = timing.first_paint_after_back_forward_cache_restore;
+ DCHECK(!first_paint.is_zero());
+ if (page_load_metrics::
+ WasStartedInForegroundOptionalEventInForegroundAfterBackForwardCacheRestore(
+ first_paint, GetDelegate(), index)) {
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramFirstPaintAfterBackForwardCacheRestore,
+ first_paint);
+ }
+}
+
+void BackForwardCachePageLoadMetricsObserver::
+ OnFirstInputAfterBackForwardCacheRestoreInPage(
+ const page_load_metrics::mojom::BackForwardCacheTiming& timing,
+ size_t index) {
+ auto first_input_delay =
+ timing.first_input_delay_after_back_forward_cache_restore;
+ DCHECK(first_input_delay.has_value());
+ if (page_load_metrics::
+ WasStartedInForegroundOptionalEventInForegroundAfterBackForwardCacheRestore(
+ first_input_delay, GetDelegate(), index)) {
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ internal::kHistogramFirstInputDelayAfterBackForwardCacheRestore,
+ *first_input_delay, base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromSeconds(60), 50);
+ }
+}
diff --git a/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h
new file mode 100644
index 00000000000..ddcb5ede44f
--- /dev/null
+++ b/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h
@@ -0,0 +1,38 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_BACK_FORWARD_CACHE_PAGE_LOAD_METRICS_OBSERVER_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_BACK_FORWARD_CACHE_PAGE_LOAD_METRICS_OBSERVER_H_
+
+#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
+
+namespace internal {
+
+extern const char kHistogramFirstPaintAfterBackForwardCacheRestore[];
+extern const char kHistogramFirstInputDelayAfterBackForwardCacheRestore[];
+
+} // namespace internal
+
+class BackForwardCachePageLoadMetricsObserver
+ : public page_load_metrics::PageLoadMetricsObserver {
+ public:
+ BackForwardCachePageLoadMetricsObserver();
+ ~BackForwardCachePageLoadMetricsObserver() override;
+
+ // page_load_metrics::PageLoadMetricsObserver:
+ page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+ OnEnterBackForwardCache(
+ const page_load_metrics::mojom::PageLoadTiming& timing) override;
+ void OnFirstPaintAfterBackForwardCacheRestoreInPage(
+ const page_load_metrics::mojom::BackForwardCacheTiming& timing,
+ size_t index) override;
+ void OnFirstInputAfterBackForwardCacheRestoreInPage(
+ const page_load_metrics::mojom::BackForwardCacheTiming& timing,
+ size_t index) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BackForwardCachePageLoadMetricsObserver);
+};
+
+#endif // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_BACK_FORWARD_CACHE_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
index a885037b114..a6f18f2ca29 100644
--- a/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
+++ b/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
@@ -12,6 +12,7 @@
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h"
#include "base/power_monitor/power_monitor.h"
+#include "components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h"
#include "components/page_load_metrics/browser/page_load_metrics_util.h"
#include "content/public/common/process_type.h"
#include "net/http/http_response_headers.h"
@@ -112,6 +113,18 @@ const char kHistogramLargestContentfulPaintMainFrame[] =
const char kHistogramLargestContentfulPaintMainFrameContentType[] =
"PageLoad.Internal.PaintTiming.LargestContentfulPaint.MainFrame."
"ContentType";
+const char kHistogramExperimentalLargestContentfulPaint[] =
+ "PageLoad.PaintTiming.NavigationToExperimentalLargestContentfulPaint";
+const char kHistogramExperimentalLargestContentfulPaintContentType[] =
+ "PageLoad.Internal.PaintTiming.ExperimentalLargestContentfulPaint."
+ "ContentType";
+const char kHistogramExperimentalLargestContentfulPaintMainFrame[] =
+ "PageLoad.PaintTiming.NavigationToExperimentalLargestContentfulPaint."
+ "MainFrame";
+const char kHistogramExperimentalLargestContentfulPaintMainFrameContentType[] =
+ "PageLoad.Internal.PaintTiming.ExperimentalLargestContentfulPaint."
+ "MainFrame."
+ "ContentType";
const char kHistogramFirstInputDelay[] =
"PageLoad.InteractiveTiming.FirstInputDelay4";
const char kHistogramFirstInputTimestamp[] =
@@ -291,6 +304,12 @@ const char kHistogramFontPreloadFirstContentfulPaint[] =
const char kHistogramFontPreloadLargestContentfulPaint[] =
"PageLoad.Clients.FontPreload.PaintTiming."
"NavigationToLargestContentfulPaint";
+const char kHistogramFontPreloadLargestImagePaint[] =
+ "PageLoad.Clients.FontPreload.PaintTiming."
+ "NavigationToLargestImagePaint";
+const char kHistogramFontPreloadLargestTextPaint[] =
+ "PageLoad.Clients.FontPreload.PaintTiming."
+ "NavigationToLargestTextPaint";
// Navigation metrics from the navigation start.
const char kHistogramNavigationTimingNavigationStartToFirstRequestStart[] =
@@ -298,11 +317,46 @@ const char kHistogramNavigationTimingNavigationStartToFirstRequestStart[] =
const char kHistogramNavigationTimingNavigationStartToFirstResponseStart[] =
"PageLoad.Experimental.NavigationTiming."
"NavigationStartToFirstResponseStart";
+const char kHistogramNavigationTimingNavigationStartToFirstLoaderCallback[] =
+ "PageLoad.Experimental.NavigationTiming."
+ "NavigationStartToFirstLoaderCallback";
+const char kHistogramNavigationTimingNavigationStartToFinalRequestStart[] =
+ "PageLoad.Experimental.NavigationTiming.NavigationStartToFinalRequestStart";
+const char kHistogramNavigationTimingNavigationStartToFinalResponseStart[] =
+ "PageLoad.Experimental.NavigationTiming."
+ "NavigationStartToFinalResponseStart";
+const char kHistogramNavigationTimingNavigationStartToFinalLoaderCallback[] =
+ "PageLoad.Experimental.NavigationTiming."
+ "NavigationStartToFinalLoaderCallback";
+const char kHistogramNavigationTimingNavigationStartToNavigationCommitSent[] =
+ "PageLoad.Experimental.NavigationTiming."
+ "NavigationStartToNavigationCommitSent";
// Navigation metrics between milestones.
const char kHistogramNavigationTimingFirstRequestStartToFirstResponseStart[] =
"PageLoad.Experimental.NavigationTiming."
"FirstRequestStartToFirstResponseStart";
+const char kHistogramNavigationTimingFirstResponseStartToFirstLoaderCallback[] =
+ "PageLoad.Experimental.NavigationTiming."
+ "FirstResponseStartToFirstLoaderCallback";
+const char kHistogramNavigationTimingFinalRequestStartToFinalResponseStart[] =
+ "PageLoad.Experimental.NavigationTiming."
+ "FinalRequestStartToFinalResponseStart";
+const char kHistogramNavigationTimingFinalResponseStartToFinalLoaderCallback[] =
+ "PageLoad.Experimental.NavigationTiming."
+ "FinalResponseStartToFinalLoaderCallback";
+const char
+ kHistogramNavigationTimingFinalLoaderCallbackToNavigationCommitSent[] =
+ "PageLoad.Experimental.NavigationTiming."
+ "FinalLoaderCallbackToNavigationCommitSent";
+
+// 103 Early Hints metrics for experiment (https://crbug.com/1093693).
+const char kHistogramEarlyHintsFirstRequestStartToEarlyHints[] =
+ "PageLoad.Experimental.EarlyHints.FirstRequestStartToEarlyHints";
+const char kHistogramEarlyHintsFinalRequestStartToEarlyHints[] =
+ "PageLoad.Experimental.EarlyHints.FinalRequestStartToEarlyHints";
+const char kHistogramEarlyHintsEarlyHintsToFinalResponseStart[] =
+ "PageLoad.Experimental.EarlyHints.EarlyHintsToFinalResponseStart";
} // namespace internal
@@ -314,8 +368,7 @@ CorePageLoadMetricsObserver::CorePageLoadMetricsObserver()
cache_bytes_(0),
network_bytes_(0),
network_bytes_including_headers_(0),
- redirect_chain_size_(0),
- largest_contentful_paint_handler_() {}
+ redirect_chain_size_(0) {}
CorePageLoadMetricsObserver::~CorePageLoadMetricsObserver() {}
@@ -339,7 +392,7 @@ CorePageLoadMetricsObserver::OnCommit(
}
UMA_HISTOGRAM_COUNTS_100("PageLoad.Navigation.RedirectChainLength",
redirect_chain_size_);
- RecordNavigationTimingHistograms(navigation_handle);
+ navigation_handle_timing_ = navigation_handle->GetNavigationHandleTiming();
return CONTINUE_OBSERVING;
}
@@ -678,6 +731,7 @@ void CorePageLoadMetricsObserver::OnParseStop(
void CorePageLoadMetricsObserver::OnComplete(
const page_load_metrics::mojom::PageLoadTiming& timing) {
+ RecordNavigationTimingHistograms();
RecordTimingHistograms(timing);
RecordByteAndResourceHistograms(timing);
RecordCpuUsageHistograms();
@@ -692,6 +746,7 @@ CorePageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
// flow. After this method is invoked, Chrome may be killed without further
// notification, so we record final metrics collected up to this point.
if (GetDelegate().DidCommit()) {
+ RecordNavigationTimingHistograms();
RecordTimingHistograms(timing);
RecordByteAndResourceHistograms(timing);
RecordCpuUsageHistograms();
@@ -770,42 +825,113 @@ void CorePageLoadMetricsObserver::OnResourceDataUseObserved(
}
}
-void CorePageLoadMetricsObserver::RecordNavigationTimingHistograms(
- content::NavigationHandle* navigation_handle) {
- // Record only main frame navigation.
- DCHECK(navigation_handle->IsInMainFrame());
+void CorePageLoadMetricsObserver::RecordNavigationTimingHistograms() {
+ const base::TimeTicks navigation_start_time =
+ GetDelegate().GetNavigationStart();
+ const content::NavigationHandleTiming& timing = navigation_handle_timing_;
// Record metrics for navigation only when all relevant milestones are
// recorded and in the expected order. It is allowed that they have the same
// value for some cases (e.g., internal redirection for HSTS).
- if (navigation_handle->NavigationStart().is_null() ||
- navigation_handle->FirstRequestStart().is_null() ||
- navigation_handle->FirstResponseStart().is_null())
+ if (navigation_start_time.is_null() ||
+ timing.first_request_start_time.is_null() ||
+ timing.first_response_start_time.is_null() ||
+ timing.first_loader_callback_time.is_null() ||
+ timing.final_request_start_time.is_null() ||
+ timing.final_response_start_time.is_null() ||
+ timing.final_loader_callback_time.is_null() ||
+ timing.navigation_commit_sent_time.is_null()) {
return;
+ }
// TODO(https://crbug.com/1076710): Change these early-returns to DCHECKs
// after the issue 1076710 is fixed.
- if (navigation_handle->NavigationStart() >
- navigation_handle->FirstRequestStart() ||
- navigation_handle->FirstRequestStart() >
- navigation_handle->FirstResponseStart()) {
+ if (navigation_start_time > timing.first_request_start_time ||
+ timing.first_request_start_time > timing.first_response_start_time ||
+ timing.first_response_start_time > timing.first_loader_callback_time ||
+ timing.first_loader_callback_time > timing.navigation_commit_sent_time) {
+ return;
+ }
+ if (navigation_start_time > timing.final_request_start_time ||
+ timing.final_request_start_time > timing.final_response_start_time ||
+ timing.final_response_start_time > timing.final_loader_callback_time ||
+ timing.final_loader_callback_time > timing.navigation_commit_sent_time) {
return;
}
+ DCHECK_LE(timing.first_request_start_time, timing.final_request_start_time);
+ DCHECK_LE(timing.first_response_start_time, timing.final_response_start_time);
+ DCHECK_LE(timing.first_loader_callback_time,
+ timing.final_loader_callback_time);
// Record the elapsed time from the navigation start milestone.
PAGE_LOAD_HISTOGRAM(
internal::kHistogramNavigationTimingNavigationStartToFirstRequestStart,
- navigation_handle->FirstRequestStart() -
- navigation_handle->NavigationStart());
+ timing.first_request_start_time - navigation_start_time);
PAGE_LOAD_HISTOGRAM(
internal::kHistogramNavigationTimingNavigationStartToFirstResponseStart,
- navigation_handle->FirstResponseStart() -
- navigation_handle->NavigationStart());
+ timing.first_response_start_time - navigation_start_time);
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramNavigationTimingNavigationStartToFirstLoaderCallback,
+ timing.first_loader_callback_time - navigation_start_time);
+
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramNavigationTimingNavigationStartToFinalRequestStart,
+ timing.final_request_start_time - navigation_start_time);
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramNavigationTimingNavigationStartToFinalResponseStart,
+ timing.final_response_start_time - navigation_start_time);
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramNavigationTimingNavigationStartToFinalLoaderCallback,
+ timing.final_loader_callback_time - navigation_start_time);
+
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramNavigationTimingNavigationStartToNavigationCommitSent,
+ timing.navigation_commit_sent_time - navigation_start_time);
// Record the intervals between milestones.
PAGE_LOAD_HISTOGRAM(
internal::kHistogramNavigationTimingFirstRequestStartToFirstResponseStart,
- navigation_handle->FirstResponseStart() -
- navigation_handle->FirstRequestStart());
+ timing.first_response_start_time - timing.first_request_start_time);
+ PAGE_LOAD_HISTOGRAM(
+ internal::
+ kHistogramNavigationTimingFirstResponseStartToFirstLoaderCallback,
+ timing.first_loader_callback_time - timing.first_response_start_time);
+
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramNavigationTimingFinalRequestStartToFinalResponseStart,
+ timing.final_response_start_time - timing.final_request_start_time);
+ PAGE_LOAD_HISTOGRAM(
+ internal::
+ kHistogramNavigationTimingFinalResponseStartToFinalLoaderCallback,
+ timing.final_loader_callback_time - timing.final_response_start_time);
+
+ PAGE_LOAD_HISTOGRAM(
+ internal::
+ kHistogramNavigationTimingFinalLoaderCallbackToNavigationCommitSent,
+ timing.navigation_commit_sent_time - timing.final_loader_callback_time);
+
+ // Record the following intervals for the 103 Early Hints experiment
+ // (https://crbug.com/1093693).
+ // - The first request start to the 103 response,
+ // - The final request start to the 103 response, and the 103 response to the
+ // final response,
+ // Note that multiple 103 responses can be served per request. These metrics
+ // use the first 103 response as the timing.
+ if (!timing.early_hints_for_first_request_time.is_null()) {
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramEarlyHintsFirstRequestStartToEarlyHints,
+ timing.first_request_start_time -
+ timing.early_hints_for_first_request_time);
+ }
+ if (!timing.early_hints_for_final_request_time.is_null()) {
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramEarlyHintsFinalRequestStartToEarlyHints,
+ timing.final_request_start_time -
+ timing.early_hints_for_final_request_time);
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramEarlyHintsEarlyHintsToFinalResponseStart,
+ timing.early_hints_for_final_request_time -
+ timing.final_response_start_time);
+ }
}
// This method records values for metrics that were not recorded during any
@@ -823,8 +949,34 @@ void CorePageLoadMetricsObserver::RecordTimingHistograms(
}
const page_load_metrics::ContentfulPaintTimingInfo&
+ main_frame_largest_image_paint = GetDelegate()
+ .GetLargestContentfulPaintHandler()
+ .MainFrameLargestImagePaint();
+ if (main_frame_largest_image_paint.ContainsValidTime() &&
+ WasStartedInForegroundOptionalEventInForeground(
+ main_frame_largest_image_paint.Time(), GetDelegate()) &&
+ font_preload_started_before_rendering_observed_) {
+ PAGE_LOAD_HISTOGRAM(internal::kHistogramFontPreloadLargestImagePaint,
+ main_frame_largest_image_paint.Time().value());
+ }
+
+ const page_load_metrics::ContentfulPaintTimingInfo&
+ main_frame_largest_text_paint = GetDelegate()
+ .GetLargestContentfulPaintHandler()
+ .MainFrameLargestTextPaint();
+ if (main_frame_largest_text_paint.ContainsValidTime() &&
+ WasStartedInForegroundOptionalEventInForeground(
+ main_frame_largest_text_paint.Time(), GetDelegate()) &&
+ font_preload_started_before_rendering_observed_) {
+ PAGE_LOAD_HISTOGRAM(internal::kHistogramFontPreloadLargestTextPaint,
+ main_frame_largest_text_paint.Time().value());
+ }
+
+ const page_load_metrics::ContentfulPaintTimingInfo&
main_frame_largest_contentful_paint =
- largest_contentful_paint_handler_.MainFrameLargestContentfulPaint();
+ GetDelegate()
+ .GetLargestContentfulPaintHandler()
+ .MainFrameLargestContentfulPaint();
if (main_frame_largest_contentful_paint.ContainsValidTime() &&
WasStartedInForegroundOptionalEventInForeground(
main_frame_largest_contentful_paint.Time(), GetDelegate())) {
@@ -837,7 +989,9 @@ void CorePageLoadMetricsObserver::RecordTimingHistograms(
const page_load_metrics::ContentfulPaintTimingInfo&
all_frames_largest_contentful_paint =
- largest_contentful_paint_handler_.MergeMainFrameAndSubframes();
+ GetDelegate()
+ .GetLargestContentfulPaintHandler()
+ .MergeMainFrameAndSubframes();
if (all_frames_largest_contentful_paint.ContainsValidTime() &&
WasStartedInForegroundOptionalEventInForeground(
all_frames_largest_contentful_paint.Time(), GetDelegate())) {
@@ -860,6 +1014,48 @@ void CorePageLoadMetricsObserver::RecordTimingHistograms(
}
}
+ const page_load_metrics::ContentfulPaintTimingInfo&
+ main_frame_experimental_largest_contentful_paint =
+ GetDelegate()
+ .GetExperimentalLargestContentfulPaintHandler()
+ .MainFrameLargestContentfulPaint();
+ if (main_frame_experimental_largest_contentful_paint.ContainsValidTime() &&
+ WasStartedInForegroundOptionalEventInForeground(
+ main_frame_experimental_largest_contentful_paint.Time(),
+ GetDelegate())) {
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramExperimentalLargestContentfulPaintMainFrame,
+ main_frame_experimental_largest_contentful_paint.Time().value());
+ UMA_HISTOGRAM_ENUMERATION(
+ internal::
+ kHistogramExperimentalLargestContentfulPaintMainFrameContentType,
+ main_frame_experimental_largest_contentful_paint.Type());
+ }
+
+ const page_load_metrics::ContentfulPaintTimingInfo&
+ all_frames_experimental_largest_contentful_paint =
+ GetDelegate()
+ .GetExperimentalLargestContentfulPaintHandler()
+ .MergeMainFrameAndSubframes();
+ if (all_frames_experimental_largest_contentful_paint.ContainsValidTime() &&
+ WasStartedInForegroundOptionalEventInForeground(
+ all_frames_experimental_largest_contentful_paint.Time(),
+ GetDelegate())) {
+ PAGE_LOAD_HISTOGRAM(
+ internal::kHistogramExperimentalLargestContentfulPaint,
+ all_frames_experimental_largest_contentful_paint.Time().value());
+ UMA_HISTOGRAM_ENUMERATION(
+ internal::kHistogramExperimentalLargestContentfulPaintContentType,
+ all_frames_experimental_largest_contentful_paint.Type());
+ TRACE_EVENT_MARK_WITH_TIMESTAMP1(
+ "loading",
+ "NavStartToExperimentalLargestContentfulPaint::AllFrames::UMA",
+ GetDelegate().GetNavigationStart() +
+ all_frames_experimental_largest_contentful_paint.Time().value(),
+ "data",
+ all_frames_experimental_largest_contentful_paint.DataAsTraceValue());
+ }
+
if (main_frame_timing.paint_timing->first_paint &&
!main_frame_timing.paint_timing->first_meaningful_paint) {
RecordFirstMeaningfulPaintStatus(
@@ -1001,19 +1197,6 @@ void CorePageLoadMetricsObserver::RecordCpuUsageHistograms() {
foreground_cpu_usage_);
}
-void CorePageLoadMetricsObserver::OnTimingUpdate(
- content::RenderFrameHost* subframe_rfh,
- const page_load_metrics::mojom::PageLoadTiming& timing) {
- largest_contentful_paint_handler_.RecordTiming(timing.paint_timing,
- subframe_rfh);
-}
-
-void CorePageLoadMetricsObserver::OnDidFinishSubFrameNavigation(
- content::NavigationHandle* navigation_handle) {
- largest_contentful_paint_handler_.OnDidFinishSubFrameNavigation(
- navigation_handle, GetDelegate());
-}
-
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
CorePageLoadMetricsObserver::OnEnterBackForwardCache(
const page_load_metrics::mojom::PageLoadTiming& timing) {
diff --git a/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h
index 0ffaa0c8704..3fb6e3c42ec 100644
--- a/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h
@@ -6,7 +6,6 @@
#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_CORE_PAGE_LOAD_METRICS_OBSERVER_H_
#include "components/page_load_metrics/browser/observers/click_input_tracker.h"
-#include "components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
#include "services/metrics/public/cpp/ukm_source.h"
@@ -32,6 +31,11 @@ extern const char kHistogramLargestContentfulPaint[];
extern const char kHistogramLargestContentfulPaintContentType[];
extern const char kHistogramLargestContentfulPaintMainFrame[];
extern const char kHistogramLargestContentfulPaintMainFrameContentType[];
+extern const char kHistogramExperimentalLargestContentfulPaint[];
+extern const char kHistogramExperimentalLargestContentfulPaintContentType[];
+extern const char kHistogramExperimentalLargestContentfulPaintMainFrame[];
+extern const char
+ kHistogramExperimentalLargestContentfulPaintMainFrameContentType[];
extern const char kHistogramParseDuration[];
extern const char kHistogramParseBlockedOnScriptLoad[];
extern const char kHistogramParseBlockedOnScriptExecution[];
@@ -105,16 +109,41 @@ extern const char kHistogramBackForwardCacheEvent[];
extern const char kHistogramFontPreloadFirstPaint[];
extern const char kHistogramFontPreloadFirstContentfulPaint[];
extern const char kHistogramFontPreloadLargestContentfulPaint[];
+extern const char kHistogramFontPreloadLargestImagePaint[];
+extern const char kHistogramFontPreloadLargestTextPaint[];
// Navigation metrics from the navigation start.
extern const char
kHistogramNavigationTimingNavigationStartToFirstRequestStart[];
extern const char
kHistogramNavigationTimingNavigationStartToFirstResponseStart[];
+extern const char
+ kHistogramNavigationTimingNavigationStartToFirstLoaderCallback[];
+extern const char
+ kHistogramNavigationTimingNavigationStartToFinalRequestStart[];
+extern const char
+ kHistogramNavigationTimingNavigationStartToFinalResponseStart[];
+extern const char
+ kHistogramNavigationTimingNavigationStartToFinalLoaderCallback[];
+extern const char
+ kHistogramNavigationTimingNavigationStartToNavigationCommitSent[];
// Navigation metrics between milestones.
extern const char
kHistogramNavigationTimingFirstRequestStartToFirstResponseStart[];
+extern const char
+ kHistogramNavigationTimingFirstResponseStartToFirstLoaderCallback[];
+extern const char
+ kHistogramNavigationTimingFinalRequestStartToFinalResponseStart[];
+extern const char
+ kHistogramNavigationTimingFinalResponseStartToFinalLoaderCallback[];
+extern const char
+ kHistogramNavigationTimingFinalLoaderCallbackToNavigationCommitSent[];
+
+// 103 Early Hints metrics for experiment (https://crbug.com/1093693).
+extern const char kHistogramEarlyHintsFirstRequestStartToEarlyHints[];
+extern const char kHistogramEarlyHintsFinalRequestStartToEarlyHints[];
+extern const char kHistogramEarlyHintsEarlyHintsToFinalResponseStart[];
enum FirstMeaningfulPaintStatus {
FIRST_MEANINGFUL_PAINT_RECORDED,
@@ -181,14 +210,9 @@ class CorePageLoadMetricsObserver
content::RenderFrameHost* rfh,
const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
resources) override;
- void OnTimingUpdate(
- content::RenderFrameHost* subframe_rfh,
- const page_load_metrics::mojom::PageLoadTiming& timing) override;
void OnCpuTimingUpdate(
content::RenderFrameHost* subframe_rfh,
const page_load_metrics::mojom::CpuTiming& timing) override;
- void OnDidFinishSubFrameNavigation(
- content::NavigationHandle* navigation_handle) override;
ObservePolicy OnEnterBackForwardCache(
const page_load_metrics::mojom::PageLoadTiming& timing) override;
void OnRestoreFromBackForwardCache(
@@ -197,8 +221,7 @@ class CorePageLoadMetricsObserver
int behavior_flags) override;
private:
- void RecordNavigationTimingHistograms(
- content::NavigationHandle* navigation_handle);
+ void RecordNavigationTimingHistograms();
void RecordTimingHistograms(
const page_load_metrics::mojom::PageLoadTiming& main_frame_timing);
void RecordByteAndResourceHistograms(
@@ -208,6 +231,8 @@ class CorePageLoadMetricsObserver
const page_load_metrics::mojom::PageLoadTiming& timing,
base::TimeTicks app_background_time);
+ content::NavigationHandleTiming navigation_handle_timing_;
+
ui::PageTransition transition_;
bool was_no_store_main_resource_;
@@ -244,9 +269,6 @@ class CorePageLoadMetricsObserver
base::TimeTicks first_paint_;
- page_load_metrics::LargestContentfulPaintHandler
- largest_contentful_paint_handler_;
-
// Tracks user input clicks for possible click burst.
page_load_metrics::ClickInputTracker click_tracker_;
diff --git a/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc b/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
index f9e7623e6b0..123f97f986e 100644
--- a/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
@@ -8,6 +8,7 @@
#include "base/test/power_monitor_test_base.h"
#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
+#include "components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h"
#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h"
#include "components/page_load_metrics/browser/page_load_metrics_util.h"
#include "components/page_load_metrics/browser/page_load_tracker.h"
@@ -22,7 +23,7 @@ using content::NavigationSimulator;
using content::RenderFrameHost;
using content::RenderFrameHostTester;
using LargestContentType =
- page_load_metrics::PageLoadMetricsObserver::LargestContentType;
+ page_load_metrics::ContentfulPaintTimingInfo::LargestContentType;
namespace {
@@ -57,6 +58,101 @@ class CorePageLoadMetricsObserverTest
page_load_metrics::mojom::CpuTiming cpu_timing(cpu_time_spent);
tester()->SimulateCpuTimingUpdate(cpu_timing, render_frame_host);
}
+
+ void TestNoLCP() {
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramLargestContentfulPaint, 0);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramLargestContentfulPaintContentType, 0);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramLargestContentfulPaintMainFrame, 0);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramLargestContentfulPaintMainFrameContentType, 0);
+
+ // Experimental values
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramExperimentalLargestContentfulPaint, 0);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramExperimentalLargestContentfulPaintContentType, 0);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramExperimentalLargestContentfulPaintMainFrame, 0);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::
+ kHistogramExperimentalLargestContentfulPaintMainFrameContentType,
+ 0);
+ }
+
+ void TestAllFramesLCP(int value, LargestContentType type) {
+ EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+ internal::kHistogramLargestContentfulPaint),
+ testing::ElementsAre(base::Bucket(value, 1)));
+ EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+ internal::kHistogramLargestContentfulPaintContentType),
+ testing::ElementsAre(base::Bucket(
+ static_cast<base::HistogramBase::Sample>(type), 1)));
+
+ // Experimental values
+ EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+ internal::kHistogramExperimentalLargestContentfulPaint),
+ testing::ElementsAre(base::Bucket(value, 1)));
+ EXPECT_THAT(
+ tester()->histogram_tester().GetAllSamples(
+ internal::kHistogramExperimentalLargestContentfulPaintContentType),
+ testing::ElementsAre(
+ base::Bucket(static_cast<base::HistogramBase::Sample>(type), 1)));
+ }
+
+ void TestMainFrameLCP(int value, LargestContentType type) {
+ EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+ internal::kHistogramLargestContentfulPaintMainFrame),
+ testing::ElementsAre(base::Bucket(value, 1)));
+ EXPECT_THAT(
+ tester()->histogram_tester().GetAllSamples(
+ internal::kHistogramLargestContentfulPaintMainFrameContentType),
+ testing::ElementsAre(
+ base::Bucket(static_cast<base::HistogramBase::Sample>(type), 1)));
+
+ // Experimental values
+ EXPECT_THAT(
+ tester()->histogram_tester().GetAllSamples(
+ internal::kHistogramExperimentalLargestContentfulPaintMainFrame),
+ testing::ElementsAre(base::Bucket(value, 1)));
+ EXPECT_THAT(
+ tester()->histogram_tester().GetAllSamples(
+ internal::
+ kHistogramExperimentalLargestContentfulPaintMainFrameContentType),
+ testing::ElementsAre(
+ base::Bucket(static_cast<base::HistogramBase::Sample>(type), 1)));
+ }
+
+ void TestEmptyMainFrameLCP() {
+ EXPECT_TRUE(
+ tester()
+ ->histogram_tester()
+ .GetAllSamples(internal::kHistogramLargestContentfulPaintMainFrame)
+ .empty());
+ EXPECT_TRUE(
+ tester()
+ ->histogram_tester()
+ .GetAllSamples(
+ internal::kHistogramLargestContentfulPaintMainFrameContentType)
+ .empty());
+
+ // Experimental LCP histograms
+ EXPECT_TRUE(
+ tester()
+ ->histogram_tester()
+ .GetAllSamples(
+ internal::kHistogramExperimentalLargestContentfulPaintMainFrame)
+ .empty());
+ EXPECT_TRUE(
+ tester()
+ ->histogram_tester()
+ .GetAllSamples(
+ internal::
+ kHistogramExperimentalLargestContentfulPaintMainFrameContentType)
+ .empty());
+ }
};
TEST_F(CorePageLoadMetricsObserverTest, NoMetrics) {
@@ -508,18 +604,31 @@ TEST_F(CorePageLoadMetricsObserverTest, ForwardBack) {
TEST_F(CorePageLoadMetricsObserverTest, NavigationTiming) {
GURL url(kDefaultTestUrl);
tester()->NavigateWithPageTransitionAndCommit(url, ui::PAGE_TRANSITION_LINK);
+ tester()->NavigateToUntrackedUrl();
// Verify if the elapsed times from the navigation start are recorded.
std::vector<const char*> metrics_from_navigation_start = {
internal::kHistogramNavigationTimingNavigationStartToFirstRequestStart,
- internal::kHistogramNavigationTimingNavigationStartToFirstResponseStart};
+ internal::kHistogramNavigationTimingNavigationStartToFirstResponseStart,
+ internal::kHistogramNavigationTimingNavigationStartToFirstLoaderCallback,
+ internal::kHistogramNavigationTimingNavigationStartToFinalRequestStart,
+ internal::kHistogramNavigationTimingNavigationStartToFinalResponseStart,
+ internal::kHistogramNavigationTimingNavigationStartToFinalLoaderCallback,
+ internal::
+ kHistogramNavigationTimingNavigationStartToNavigationCommitSent};
for (const char* metric : metrics_from_navigation_start)
tester()->histogram_tester().ExpectTotalCount(metric, 1);
// Verify if the intervals between adjacent milestones are recorded.
std::vector<const char*> metrics_between_milestones = {
+ internal::kHistogramNavigationTimingFirstRequestStartToFirstResponseStart,
+ internal::
+ kHistogramNavigationTimingFirstResponseStartToFirstLoaderCallback,
+ internal::kHistogramNavigationTimingFinalRequestStartToFinalResponseStart,
internal::
- kHistogramNavigationTimingFirstRequestStartToFirstResponseStart};
+ kHistogramNavigationTimingFinalResponseStartToFinalLoaderCallback,
+ internal::
+ kHistogramNavigationTimingFinalLoaderCallbackToNavigationCommitSent};
for (const char* metric : metrics_between_milestones)
tester()->histogram_tester().ExpectTotalCount(metric, 1);
}
@@ -659,13 +768,16 @@ TEST_F(CorePageLoadMetricsObserverTest, LargestImageLoading) {
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Largest image is loading so its timestamp is TimeDelta().
- timing.paint_timing->largest_image_paint = base::TimeDelta();
- timing.paint_timing->largest_image_paint_size = 100u;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint =
+ base::TimeDelta();
+ timing.paint_timing->largest_contentful_paint->largest_image_paint_size =
+ 100u;
// There is a text paint but it's smaller than image. Pick a value that lines
// up with a histogram bucket.
- timing.paint_timing->largest_text_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(4780);
- timing.paint_timing->largest_text_paint_size = 70u;
+ timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 70u;
+ PopulateExperimentalLCP(timing.paint_timing);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
@@ -674,8 +786,7 @@ TEST_F(CorePageLoadMetricsObserverTest, LargestImageLoading) {
NavigateAndCommit(GURL(kDefaultTestUrl2));
// The image was larger so LCP should NOT be reported.
- tester()->histogram_tester().ExpectTotalCount(
- internal::kHistogramLargestContentfulPaint, 0);
+ TestNoLCP();
}
TEST_F(CorePageLoadMetricsObserverTest, LargestImageLoadingSmallerThanText) {
@@ -683,13 +794,16 @@ TEST_F(CorePageLoadMetricsObserverTest, LargestImageLoadingSmallerThanText) {
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Largest image is loading so its timestamp is TimeDelta().
- timing.paint_timing->largest_image_paint = base::TimeDelta();
- timing.paint_timing->largest_image_paint_size = 100u;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint =
+ base::TimeDelta();
+ timing.paint_timing->largest_contentful_paint->largest_image_paint_size =
+ 100u;
// There is a text paint but it's smaller than image. Pick a value that lines
// up with a histogram bucket.
- timing.paint_timing->largest_text_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(4780);
- timing.paint_timing->largest_text_paint_size = 120u;
+ timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 120u;
+ PopulateExperimentalLCP(timing.paint_timing);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
@@ -697,9 +811,7 @@ TEST_F(CorePageLoadMetricsObserverTest, LargestImageLoadingSmallerThanText) {
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(4780, 1)));
+ TestAllFramesLCP(4780, LargestContentType::kText);
}
TEST_F(CorePageLoadMetricsObserverTest,
@@ -716,9 +828,11 @@ TEST_F(CorePageLoadMetricsObserverTest,
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(200);
- subframe_timing.paint_timing->largest_image_paint =
+ subframe_timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
- subframe_timing.paint_timing->largest_image_paint_size = 100u;
+ subframe_timing.paint_timing->largest_contentful_paint
+ ->largest_image_paint_size = 100u;
+ PopulateExperimentalLCP(subframe_timing.paint_timing);
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
@@ -736,26 +850,8 @@ TEST_F(CorePageLoadMetricsObserverTest,
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(4780, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
- 1)));
- EXPECT_TRUE(
- tester()
- ->histogram_tester()
- .GetAllSamples(internal::kHistogramLargestContentfulPaintMainFrame)
- .empty());
- EXPECT_TRUE(
- tester()
- ->histogram_tester()
- .GetAllSamples(
- internal::kHistogramLargestContentfulPaintMainFrameContentType)
- .empty());
+ TestAllFramesLCP(4780, LargestContentType::kImage);
+ TestEmptyMainFrameLCP();
}
TEST_F(CorePageLoadMetricsObserverTest,
@@ -772,11 +868,15 @@ TEST_F(CorePageLoadMetricsObserverTest,
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(200);
- subframe_timing.paint_timing->largest_image_paint = base::TimeDelta();
- subframe_timing.paint_timing->largest_image_paint_size = 100u;
- subframe_timing.paint_timing->largest_text_paint =
+ subframe_timing.paint_timing->largest_contentful_paint->largest_image_paint =
+ base::TimeDelta();
+ subframe_timing.paint_timing->largest_contentful_paint
+ ->largest_image_paint_size = 100u;
+ subframe_timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(500);
- subframe_timing.paint_timing->largest_text_paint_size = 80u;
+ subframe_timing.paint_timing->largest_contentful_paint
+ ->largest_text_paint_size = 80u;
+ PopulateExperimentalLCP(subframe_timing.paint_timing);
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
@@ -794,14 +894,7 @@ TEST_F(CorePageLoadMetricsObserverTest,
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- tester()->histogram_tester().ExpectTotalCount(
- internal::kHistogramLargestContentfulPaint, 0);
- tester()->histogram_tester().ExpectTotalCount(
- internal::kHistogramLargestContentfulPaintContentType, 0);
- tester()->histogram_tester().ExpectTotalCount(
- internal::kHistogramLargestContentfulPaintMainFrame, 0);
- tester()->histogram_tester().ExpectTotalCount(
- internal::kHistogramLargestContentfulPaintMainFrameContentType, 0);
+ TestNoLCP();
}
TEST_F(CorePageLoadMetricsObserverTest,
@@ -812,9 +905,10 @@ TEST_F(CorePageLoadMetricsObserverTest,
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
- timing.paint_timing->largest_image_paint =
+ timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
- timing.paint_timing->largest_image_paint_size = 50u;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint_size = 50u;
+ PopulateExperimentalLCP(timing.paint_timing);
PopulateRequiredTimingFields(&timing);
page_load_metrics::mojom::PageLoadTiming subframe_timing;
@@ -838,24 +932,8 @@ TEST_F(CorePageLoadMetricsObserverTest,
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(4780, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
- 1)));
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintMainFrame),
- testing::ElementsAre(base::Bucket(4780, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintMainFrameContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
- 1)));
+ TestAllFramesLCP(4780, LargestContentType::kImage);
+ TestMainFrameLCP(4780, LargestContentType::kImage);
}
// This is to test whether LargestContentfulPaintAllFrames could merge
@@ -870,18 +948,21 @@ TEST_F(CorePageLoadMetricsObserverTest,
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
- timing.paint_timing->largest_image_paint =
+ timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(9382);
- timing.paint_timing->largest_image_paint_size = 50u;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint_size = 50u;
+ PopulateExperimentalLCP(timing.paint_timing);
PopulateRequiredTimingFields(&timing);
// Create a candidate in subframe with a larger size.
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(2);
- subframe_timing.paint_timing->largest_image_paint =
+ subframe_timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
- subframe_timing.paint_timing->largest_image_paint_size = 100u;
+ subframe_timing.paint_timing->largest_contentful_paint
+ ->largest_image_paint_size = 100u;
+ PopulateExperimentalLCP(subframe_timing.paint_timing);
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
@@ -899,25 +980,8 @@ TEST_F(CorePageLoadMetricsObserverTest,
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(4780, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
- 1)));
-
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintMainFrame),
- testing::ElementsAre(base::Bucket(9382, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintMainFrameContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
- 1)));
+ TestAllFramesLCP(4780, LargestContentType::kImage);
+ TestMainFrameLCP(9382, LargestContentType::kImage);
}
// This is to test whether LargestContentfulPaintAllFrames could merge
@@ -931,18 +995,21 @@ TEST_F(CorePageLoadMetricsObserverTest,
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
- timing.paint_timing->largest_text_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(4780);
- timing.paint_timing->largest_text_paint_size = 100u;
+ timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 100u;
+ PopulateExperimentalLCP(timing.paint_timing);
PopulateRequiredTimingFields(&timing);
// Create a candidate in subframe with a smaller size.
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(2);
- subframe_timing.paint_timing->largest_text_paint =
+ subframe_timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(300);
- subframe_timing.paint_timing->largest_text_paint_size = 50u;
+ subframe_timing.paint_timing->largest_contentful_paint
+ ->largest_text_paint_size = 50u;
+ PopulateExperimentalLCP(subframe_timing.paint_timing);
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
@@ -960,25 +1027,8 @@ TEST_F(CorePageLoadMetricsObserverTest,
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(4780, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
- 1)));
-
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintMainFrame),
- testing::ElementsAre(base::Bucket(4780, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintMainFrameContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
- 1)));
+ TestAllFramesLCP(4780, LargestContentType::kText);
+ TestMainFrameLCP(4780, LargestContentType::kText);
}
// This tests a trade-off we have made - aggregating all subframe candidates,
@@ -997,9 +1047,11 @@ TEST_F(CorePageLoadMetricsObserverTest,
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(2);
- subframe_timing.paint_timing->largest_image_paint =
+ subframe_timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
- subframe_timing.paint_timing->largest_image_paint_size = 50u;
+ subframe_timing.paint_timing->largest_contentful_paint
+ ->largest_image_paint_size = 50u;
+ PopulateExperimentalLCP(subframe_timing.paint_timing);
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
@@ -1014,24 +1066,18 @@ TEST_F(CorePageLoadMetricsObserverTest,
tester()->SimulateTimingUpdate(timing);
tester()->SimulateTimingUpdate(subframe_timing, subframe);
- subframe_timing.paint_timing->largest_image_paint =
+ subframe_timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(300);
- subframe_timing.paint_timing->largest_image_paint_size = 10u;
+ subframe_timing.paint_timing->largest_contentful_paint
+ ->largest_image_paint_size = 10u;
+ PopulateExperimentalLCP(subframe_timing.paint_timing);
tester()->SimulateTimingUpdate(subframe_timing, subframe);
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
// Ensure that the largest_image_paint timing for the main frame is recorded.
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(4780, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
- 1)));
+ TestAllFramesLCP(4780, LargestContentType::kImage);
}
// This tests a trade-off we have made - aggregating all subframe candidates,
@@ -1051,9 +1097,11 @@ TEST_F(
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(2);
- subframe_timing.paint_timing->largest_image_paint =
+ subframe_timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
- subframe_timing.paint_timing->largest_image_paint_size = 10u;
+ subframe_timing.paint_timing->largest_contentful_paint
+ ->largest_image_paint_size = 10u;
+ PopulateExperimentalLCP(subframe_timing.paint_timing);
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
@@ -1068,24 +1116,18 @@ TEST_F(
tester()->SimulateTimingUpdate(timing);
tester()->SimulateTimingUpdate(subframe_timing, subframe);
- subframe_timing.paint_timing->largest_image_paint =
+ subframe_timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(990);
- subframe_timing.paint_timing->largest_image_paint_size = 50u;
+ subframe_timing.paint_timing->largest_contentful_paint
+ ->largest_image_paint_size = 50u;
+ PopulateExperimentalLCP(subframe_timing.paint_timing);
tester()->SimulateTimingUpdate(subframe_timing, subframe);
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
// Ensure that the largest_image_paint timing for the main frame is recorded.
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(990, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
- 1)));
+ TestAllFramesLCP(990, LargestContentType::kImage);
}
TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_NoTextOrImage) {
@@ -1094,7 +1136,8 @@ TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_NoTextOrImage) {
timing.navigation_start = base::Time::FromDoubleT(1);
// When the size is 0, the timing is regarded as not set and should be
// excluded from recording to UMA.
- timing.paint_timing->largest_text_paint_size = 0u;
+ timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 0u;
+ PopulateExperimentalLCP(timing.paint_timing);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
@@ -1102,14 +1145,7 @@ TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_NoTextOrImage) {
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- tester()->histogram_tester().ExpectTotalCount(
- internal::kHistogramLargestContentfulPaint, 0);
- tester()->histogram_tester().ExpectTotalCount(
- internal::kHistogramLargestContentfulPaintContentType, 0);
- tester()->histogram_tester().ExpectTotalCount(
- internal::kHistogramLargestContentfulPaintMainFrame, 0);
- tester()->histogram_tester().ExpectTotalCount(
- internal::kHistogramLargestContentfulPaintMainFrameContentType, 0);
+ TestNoLCP();
}
TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_OnlyText) {
@@ -1117,9 +1153,10 @@ TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_OnlyText) {
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
- timing.paint_timing->largest_text_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(4780);
- timing.paint_timing->largest_text_paint_size = 100;
+ timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 100;
+ PopulateExperimentalLCP(timing.paint_timing);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
@@ -1127,15 +1164,7 @@ TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_OnlyText) {
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(4780, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
- 1)));
+ TestAllFramesLCP(4780, LargestContentType::kText);
}
TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_OnlyImage) {
@@ -1143,9 +1172,10 @@ TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_OnlyImage) {
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
- timing.paint_timing->largest_image_paint =
+ timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
- timing.paint_timing->largest_image_paint_size = 100;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint_size = 100;
+ PopulateExperimentalLCP(timing.paint_timing);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
@@ -1153,15 +1183,7 @@ TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_OnlyImage) {
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(4780, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
- 1)));
+ TestAllFramesLCP(4780, LargestContentType::kImage);
}
TEST_F(CorePageLoadMetricsObserverTest,
@@ -1170,12 +1192,13 @@ TEST_F(CorePageLoadMetricsObserverTest,
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
- timing.paint_timing->largest_image_paint =
+ timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
- timing.paint_timing->largest_image_paint_size = 100;
- timing.paint_timing->largest_text_paint =
+ timing.paint_timing->largest_contentful_paint->largest_image_paint_size = 100;
+ timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(1000);
- timing.paint_timing->largest_text_paint_size = 10;
+ timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 10;
+ PopulateExperimentalLCP(timing.paint_timing);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
@@ -1183,15 +1206,7 @@ TEST_F(CorePageLoadMetricsObserverTest,
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(4780, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
- 1)));
+ TestAllFramesLCP(4780, LargestContentType::kImage);
}
TEST_F(CorePageLoadMetricsObserverTest,
@@ -1199,13 +1214,14 @@ TEST_F(CorePageLoadMetricsObserverTest,
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
- timing.paint_timing->largest_image_paint =
+ timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
- timing.paint_timing->largest_image_paint_size = 10;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint_size = 10;
// Pick a value that lines up with a histogram bucket.
- timing.paint_timing->largest_text_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(990);
- timing.paint_timing->largest_text_paint_size = 100;
+ timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 100;
+ PopulateExperimentalLCP(timing.paint_timing);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
@@ -1213,15 +1229,7 @@ TEST_F(CorePageLoadMetricsObserverTest,
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
- EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaint),
- testing::ElementsAre(base::Bucket(990, 1)));
- EXPECT_THAT(
- tester()->histogram_tester().GetAllSamples(
- internal::kHistogramLargestContentfulPaintContentType),
- testing::ElementsAre(base::Bucket(
- static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
- 1)));
+ TestAllFramesLCP(990, LargestContentType::kText);
}
TEST_F(CorePageLoadMetricsObserverTest, ForegroundToFirstMeaningfulPaint) {
@@ -1423,12 +1431,13 @@ TEST_F(CorePageLoadMetricsObserverTest, FontPreloadHistogramsNotObserved) {
timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(150);
timing.paint_timing->first_contentful_paint =
base::TimeDelta::FromMilliseconds(200);
- timing.paint_timing->largest_text_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(250);
- timing.paint_timing->largest_text_paint_size = 100u;
- timing.paint_timing->largest_image_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 100u;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(250);
- timing.paint_timing->largest_image_paint_size = 100u;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint_size =
+ 100u;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL("https://www.google.com/"));
@@ -1443,6 +1452,10 @@ TEST_F(CorePageLoadMetricsObserverTest, FontPreloadHistogramsNotObserved) {
internal::kHistogramFontPreloadFirstContentfulPaint, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFontPreloadLargestContentfulPaint, 0);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramFontPreloadLargestImagePaint, 0);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramFontPreloadLargestTextPaint, 0);
}
// PageLoad.Clients.FontPreload.* should be recorded when the behavior is
@@ -1456,12 +1469,13 @@ TEST_F(CorePageLoadMetricsObserverTest, FontPreloadHistogramsObserved) {
timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(150);
timing.paint_timing->first_contentful_paint =
base::TimeDelta::FromMilliseconds(200);
- timing.paint_timing->largest_text_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(250);
- timing.paint_timing->largest_text_paint_size = 100u;
- timing.paint_timing->largest_image_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 100u;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(250);
- timing.paint_timing->largest_image_paint_size = 100u;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint_size =
+ 100u;
PopulateRequiredTimingFields(&timing);
// Note: PageLoadMetrics internally processes loading behavior before timing
@@ -1481,6 +1495,10 @@ TEST_F(CorePageLoadMetricsObserverTest, FontPreloadHistogramsObserved) {
internal::kHistogramFontPreloadFirstContentfulPaint, 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFontPreloadLargestContentfulPaint, 1);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramFontPreloadLargestImagePaint, 1);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramFontPreloadLargestTextPaint, 1);
}
// PageLoad.Clients.FontPreload.* depends on the order that, we need to first
@@ -1497,12 +1515,13 @@ TEST_F(CorePageLoadMetricsObserverTest,
timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(150);
timing.paint_timing->first_contentful_paint =
base::TimeDelta::FromMilliseconds(200);
- timing.paint_timing->largest_text_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint =
base::TimeDelta::FromMilliseconds(250);
- timing.paint_timing->largest_text_paint_size = 100u;
- timing.paint_timing->largest_image_paint =
+ timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 100u;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint =
base::TimeDelta::FromMilliseconds(250);
- timing.paint_timing->largest_image_paint_size = 100u;
+ timing.paint_timing->largest_contentful_paint->largest_image_paint_size =
+ 100u;
PopulateRequiredTimingFields(&timing);
// Note: PageLoadMetrics internally processes loading behavior before timing
@@ -1523,7 +1542,12 @@ TEST_F(CorePageLoadMetricsObserverTest,
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFontPreloadFirstContentfulPaint, 0);
- // LCP is recorded on page complete/closing, so it can still be observed.
+ // Largest*Paint is recorded on page complete/closing, so they can still be
+ // observed.
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFontPreloadLargestContentfulPaint, 1);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramFontPreloadLargestImagePaint, 1);
+ tester()->histogram_tester().ExpectTotalCount(
+ internal::kHistogramFontPreloadLargestTextPaint, 1);
}
diff --git a/chromium/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc b/chromium/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc
index c338e901f66..ea407015ecb 100644
--- a/chromium/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc
+++ b/chromium/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc
@@ -6,6 +6,7 @@
#include "components/page_load_metrics/browser/page_load_metrics_observer_delegate.h"
#include "components/page_load_metrics/common/page_load_metrics.mojom.h"
+#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
namespace page_load_metrics {
@@ -63,9 +64,8 @@ void Reset(ContentfulPaintTimingInfo& timing) {
} // namespace
-ContentfulPaintTimingInfo::ContentfulPaintTimingInfo(
- PageLoadMetricsObserver::LargestContentType type,
- bool in_main_frame)
+ContentfulPaintTimingInfo::ContentfulPaintTimingInfo(LargestContentType type,
+ bool in_main_frame)
: time_(base::Optional<base::TimeDelta>()),
size_(0),
type_(type),
@@ -73,7 +73,7 @@ ContentfulPaintTimingInfo::ContentfulPaintTimingInfo(
ContentfulPaintTimingInfo::ContentfulPaintTimingInfo(
const base::Optional<base::TimeDelta>& time,
const uint64_t& size,
- const page_load_metrics::PageLoadMetricsObserver::LargestContentType type,
+ const LargestContentType type,
bool in_main_frame)
: time_(time), size_(size), type_(type), in_main_frame_(in_main_frame) {}
@@ -93,9 +93,9 @@ ContentfulPaintTimingInfo::DataAsTraceValue() const {
std::string ContentfulPaintTimingInfo::TypeInString() const {
switch (Type()) {
- case page_load_metrics::PageLoadMetricsObserver::LargestContentType::kText:
+ case LargestContentType::kText:
return "text";
- case page_load_metrics::PageLoadMetricsObserver::LargestContentType::kImage:
+ case LargestContentType::kImage:
return "image";
default:
NOTREACHED();
@@ -114,16 +114,50 @@ void ContentfulPaintTimingInfo::Reset(
size_ = size;
time_ = time;
}
-
ContentfulPaint::ContentfulPaint(bool in_main_frame)
- : text_(PageLoadMetricsObserver::LargestContentType::kText, in_main_frame),
- image_(PageLoadMetricsObserver::LargestContentType::kImage,
+ : text_(ContentfulPaintTimingInfo::LargestContentType::kText,
+ in_main_frame),
+ image_(ContentfulPaintTimingInfo::LargestContentType::kImage,
in_main_frame) {}
-const ContentfulPaintTimingInfo& ContentfulPaint::MergeTextAndImageTiming() {
+const ContentfulPaintTimingInfo& ContentfulPaint::MergeTextAndImageTiming()
+ const {
return MergeTimingsBySizeAndTime(text_, image_);
}
+// static
+bool LargestContentfulPaintHandler::AssignTimeAndSizeForLargestContentfulPaint(
+ const page_load_metrics::mojom::LargestContentfulPaintTiming&
+ largest_contentful_paint,
+ base::Optional<base::TimeDelta>* largest_content_paint_time,
+ uint64_t* largest_content_paint_size,
+ ContentfulPaintTimingInfo::LargestContentType* largest_content_type) {
+ // Size being 0 means the paint time is not recorded.
+ if (!largest_contentful_paint.largest_text_paint_size &&
+ !largest_contentful_paint.largest_image_paint_size)
+ return false;
+
+ if ((largest_contentful_paint.largest_text_paint_size >
+ largest_contentful_paint.largest_image_paint_size) ||
+ (largest_contentful_paint.largest_text_paint_size ==
+ largest_contentful_paint.largest_image_paint_size &&
+ largest_contentful_paint.largest_text_paint <
+ largest_contentful_paint.largest_image_paint)) {
+ *largest_content_paint_time = largest_contentful_paint.largest_text_paint;
+ *largest_content_paint_size =
+ largest_contentful_paint.largest_text_paint_size;
+ *largest_content_type =
+ ContentfulPaintTimingInfo::LargestContentType::kText;
+ } else {
+ *largest_content_paint_time = largest_contentful_paint.largest_image_paint;
+ *largest_content_paint_size =
+ largest_contentful_paint.largest_image_paint_size;
+ *largest_content_type =
+ ContentfulPaintTimingInfo::LargestContentType::kImage;
+ }
+ return true;
+}
+
LargestContentfulPaintHandler::LargestContentfulPaintHandler()
: main_frame_contentful_paint_(true /*in_main_frame*/),
subframe_contentful_paint_(false /*in_main_frame*/) {}
@@ -131,10 +165,14 @@ LargestContentfulPaintHandler::LargestContentfulPaintHandler()
LargestContentfulPaintHandler::~LargestContentfulPaintHandler() = default;
void LargestContentfulPaintHandler::RecordTiming(
- const mojom::PaintTimingPtr& timing,
+ const page_load_metrics::mojom::LargestContentfulPaintTiming&
+ largest_contentful_paint,
+ const base::Optional<base::TimeDelta>&
+ first_input_or_scroll_notified_timestamp,
content::RenderFrameHost* subframe_rfh) {
if (!IsSubframe(subframe_rfh)) {
- RecordMainFrameTiming(timing);
+ RecordMainFrameTiming(largest_contentful_paint,
+ first_input_or_scroll_notified_timestamp);
return;
}
// For subframes
@@ -144,11 +182,12 @@ void LargestContentfulPaintHandler::RecordTiming(
// We received timing information for an untracked load. Ignore it.
return;
}
- RecordSubframeTiming(timing, it->second);
+ RecordSubframeTiming(largest_contentful_paint,
+ first_input_or_scroll_notified_timestamp, it->second);
}
const ContentfulPaintTimingInfo&
-LargestContentfulPaintHandler::MergeMainFrameAndSubframes() {
+LargestContentfulPaintHandler::MergeMainFrameAndSubframes() const {
const ContentfulPaintTimingInfo& main_frame_timing =
main_frame_contentful_paint_.MergeTextAndImageTiming();
const ContentfulPaintTimingInfo& subframe_timing =
@@ -165,31 +204,40 @@ LargestContentfulPaintHandler::MergeMainFrameAndSubframes() {
// trade-off we make to keep a simple algorithm, otherwise we will have to
// track one candidate per subframe.
void LargestContentfulPaintHandler::RecordSubframeTiming(
- const mojom::PaintTimingPtr& timing,
+ const page_load_metrics::mojom::LargestContentfulPaintTiming&
+ largest_contentful_paint,
+ const base::Optional<base::TimeDelta>&
+ first_input_or_scroll_notified_timestamp,
const base::TimeDelta& navigation_start_offset) {
- UpdateFirstInputOrScrollNotified(
- timing->first_input_or_scroll_notified_timestamp,
- navigation_start_offset);
+ UpdateFirstInputOrScrollNotified(first_input_or_scroll_notified_timestamp,
+ navigation_start_offset);
MergeForSubframes(&subframe_contentful_paint_.Text(),
- timing->largest_text_paint, timing->largest_text_paint_size,
+ largest_contentful_paint.largest_text_paint,
+ largest_contentful_paint.largest_text_paint_size,
navigation_start_offset);
MergeForSubframes(&subframe_contentful_paint_.Image(),
- timing->largest_image_paint,
- timing->largest_image_paint_size, navigation_start_offset);
+ largest_contentful_paint.largest_image_paint,
+ largest_contentful_paint.largest_image_paint_size,
+ navigation_start_offset);
}
void LargestContentfulPaintHandler::RecordMainFrameTiming(
- const mojom::PaintTimingPtr& timing) {
+ const page_load_metrics::mojom::LargestContentfulPaintTiming&
+ largest_contentful_paint,
+ const base::Optional<base::TimeDelta>&
+ first_input_or_scroll_notified_timestamp) {
UpdateFirstInputOrScrollNotified(
- timing->first_input_or_scroll_notified_timestamp,
+ first_input_or_scroll_notified_timestamp,
/* navigation_start_offset */ base::TimeDelta());
- if (IsValid(timing->largest_text_paint)) {
- main_frame_contentful_paint_.Text().Reset(timing->largest_text_paint,
- timing->largest_text_paint_size);
+ if (IsValid(largest_contentful_paint.largest_text_paint)) {
+ main_frame_contentful_paint_.Text().Reset(
+ largest_contentful_paint.largest_text_paint,
+ largest_contentful_paint.largest_text_paint_size);
}
- if (IsValid(timing->largest_image_paint)) {
+ if (IsValid(largest_contentful_paint.largest_image_paint)) {
main_frame_contentful_paint_.Image().Reset(
- timing->largest_image_paint, timing->largest_image_paint_size);
+ largest_contentful_paint.largest_image_paint,
+ largest_contentful_paint.largest_image_paint_size);
}
}
@@ -220,7 +268,7 @@ void LargestContentfulPaintHandler::UpdateFirstInputOrScrollNotified(
void LargestContentfulPaintHandler::OnDidFinishSubFrameNavigation(
content::NavigationHandle* navigation_handle,
- const PageLoadMetricsObserverDelegate& delegate) {
+ base::TimeTicks navigation_start) {
if (!navigation_handle->HasCommitted())
return;
@@ -229,7 +277,7 @@ void LargestContentfulPaintHandler::OnDidFinishSubFrameNavigation(
subframe_navigation_start_offset_.erase(
navigation_handle->GetFrameTreeNodeId());
- if (delegate.GetNavigationStart() > navigation_handle->NavigationStart())
+ if (navigation_start > navigation_handle->NavigationStart())
return;
base::TimeDelta navigation_delta;
// If navigation start offset tracking has been disabled for tests, then
@@ -238,13 +286,18 @@ void LargestContentfulPaintHandler::OnDidFinishSubFrameNavigation(
// See crbug/616901 for more details on why navigation start offset tracking
// is disabled in tests.
if (!g_disable_subframe_navigation_start_offset) {
- navigation_delta =
- navigation_handle->NavigationStart() - delegate.GetNavigationStart();
+ navigation_delta = navigation_handle->NavigationStart() - navigation_start;
}
subframe_navigation_start_offset_.insert(std::make_pair(
navigation_handle->GetFrameTreeNodeId(), navigation_delta));
}
+void LargestContentfulPaintHandler::OnFrameDeleted(
+ content::RenderFrameHost* render_frame_host) {
+ subframe_navigation_start_offset_.erase(
+ render_frame_host->GetFrameTreeNodeId());
+}
+
void LargestContentfulPaintHandler::MergeForSubframes(
ContentfulPaintTimingInfo* inout_timing,
const base::Optional<base::TimeDelta>& candidate_new_time,
diff --git a/chromium/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h b/chromium/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
index b0f87192f12..0ac2bcbd5eb 100644
--- a/chromium/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
+++ b/chromium/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
@@ -6,31 +6,43 @@
#include <map>
+#include "base/time/time.h"
#include "base/trace_event/traced_value.h"
-#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
#include "components/page_load_metrics/common/page_load_metrics.mojom.h"
#include "components/page_load_metrics/common/page_load_timing.h"
+namespace content {
+
+class NavigationHandle;
+class RenderFrameHost;
+
+} // namespace content
+
namespace page_load_metrics {
class ContentfulPaintTimingInfo {
public:
- explicit ContentfulPaintTimingInfo(
- page_load_metrics::PageLoadMetricsObserver::LargestContentType,
- bool in_main_frame);
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
+ enum class LargestContentType {
+ kImage = 0,
+ kText = 1,
+ kMaxValue = kText,
+ };
+
+ explicit ContentfulPaintTimingInfo(LargestContentType largest_content_type,
+ bool in_main_frame);
explicit ContentfulPaintTimingInfo(
const base::Optional<base::TimeDelta>&,
const uint64_t& size,
- const page_load_metrics::PageLoadMetricsObserver::LargestContentType,
+ const LargestContentType largest_content_type,
bool in_main_frame);
explicit ContentfulPaintTimingInfo(const ContentfulPaintTimingInfo& other);
void Reset(const base::Optional<base::TimeDelta>&, const uint64_t& size);
base::Optional<base::TimeDelta> Time() const { return time_; }
bool InMainFrame() const { return in_main_frame_; }
uint64_t Size() const { return size_; }
- page_load_metrics::PageLoadMetricsObserver::LargestContentType Type() const {
- return type_;
- }
+ LargestContentType Type() const { return type_; }
// Returns true iff this object does not represent any paint.
bool Empty() const {
@@ -52,7 +64,7 @@ class ContentfulPaintTimingInfo {
std::string TypeInString() const;
base::Optional<base::TimeDelta> time_;
uint64_t size_;
- page_load_metrics::PageLoadMetricsObserver::LargestContentType type_;
+ LargestContentType type_;
bool in_main_frame_;
};
@@ -60,8 +72,10 @@ class ContentfulPaint {
public:
explicit ContentfulPaint(bool in_main_frame);
ContentfulPaintTimingInfo& Text() { return text_; }
+ const ContentfulPaintTimingInfo& Text() const { return text_; }
ContentfulPaintTimingInfo& Image() { return image_; }
- const ContentfulPaintTimingInfo& MergeTextAndImageTiming();
+ const ContentfulPaintTimingInfo& Image() const { return image_; }
+ const ContentfulPaintTimingInfo& MergeTextAndImageTiming() const;
private:
ContentfulPaintTimingInfo text_;
@@ -70,13 +84,25 @@ class ContentfulPaint {
class LargestContentfulPaintHandler {
public:
- using FrameTreeNodeId =
- page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId;
+ using FrameTreeNodeId = int;
static void SetTestMode(bool enabled);
LargestContentfulPaintHandler();
~LargestContentfulPaintHandler();
- void RecordTiming(const page_load_metrics::mojom::PaintTimingPtr&,
- content::RenderFrameHost* subframe_rfh);
+
+ // Returns true if the out parameters are assigned values.
+ static bool AssignTimeAndSizeForLargestContentfulPaint(
+ const page_load_metrics::mojom::LargestContentfulPaintTiming&
+ largest_contentful_paint,
+ base::Optional<base::TimeDelta>* largest_content_paint_time,
+ uint64_t* largest_content_paint_size,
+ ContentfulPaintTimingInfo::LargestContentType* largest_content_type);
+
+ void RecordTiming(
+ const page_load_metrics::mojom::LargestContentfulPaintTiming&
+ largest_contentful_paint,
+ const base::Optional<base::TimeDelta>&
+ first_input_or_scroll_notified_timestamp,
+ content::RenderFrameHost* subframe_rfh);
inline void RecordMainFrameTreeNodeId(int main_frame_tree_node_id) {
main_frame_tree_node_id_.emplace(main_frame_tree_node_id);
}
@@ -87,24 +113,39 @@ class LargestContentfulPaintHandler {
// We merge the candidates from text side and image side to get the largest
// candidate across both types of content.
- const ContentfulPaintTimingInfo& MainFrameLargestContentfulPaint() {
+ const ContentfulPaintTimingInfo& MainFrameLargestContentfulPaint() const {
return main_frame_contentful_paint_.MergeTextAndImageTiming();
}
- const ContentfulPaintTimingInfo& SubframesLargestContentfulPaint() {
+ const ContentfulPaintTimingInfo& SubframesLargestContentfulPaint() const {
return subframe_contentful_paint_.MergeTextAndImageTiming();
}
+ const ContentfulPaintTimingInfo& MainFrameLargestImagePaint() const {
+ return main_frame_contentful_paint_.Image();
+ }
+ const ContentfulPaintTimingInfo& MainFrameLargestTextPaint() const {
+ return main_frame_contentful_paint_.Text();
+ }
// We merge the candidates from main frame and subframe to get the largest
// candidate across all frames.
- const ContentfulPaintTimingInfo& MergeMainFrameAndSubframes();
+ const ContentfulPaintTimingInfo& MergeMainFrameAndSubframes() const;
void OnDidFinishSubFrameNavigation(
content::NavigationHandle* navigation_handle,
- const PageLoadMetricsObserverDelegate& delegate);
+ base::TimeTicks navigation_start);
+ void OnFrameDeleted(content::RenderFrameHost* render_frame_host);
private:
- void RecordSubframeTiming(const mojom::PaintTimingPtr& timing,
- const base::TimeDelta& navigation_start_offset);
- void RecordMainFrameTiming(const page_load_metrics::mojom::PaintTimingPtr&);
+ void RecordSubframeTiming(
+ const page_load_metrics::mojom::LargestContentfulPaintTiming&
+ largest_contentful_paint,
+ const base::Optional<base::TimeDelta>&
+ first_input_or_scroll_notified_timestamp,
+ const base::TimeDelta& navigation_start_offset);
+ void RecordMainFrameTiming(
+ const page_load_metrics::mojom::LargestContentfulPaintTiming&
+ largest_contentful_paint,
+ const base::Optional<base::TimeDelta>&
+ first_input_or_scroll_notified_timestamp);
void UpdateFirstInputOrScrollNotified(
const base::Optional<base::TimeDelta>& candidate_new_time,
const base::TimeDelta& navigation_start_offset);
diff --git a/chromium/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc b/chromium/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
index 2d822f9eacf..8c4045c51a5 100644
--- a/chromium/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
+++ b/chromium/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
@@ -160,6 +160,7 @@ UseCounterPageLoadMetricsObserver::GetAllowedUkmFeatures() {
WebFeature::kThirdPartyCacheStorage,
WebFeature::kThirdPartyLocalStorage,
WebFeature::kThirdPartySessionStorage,
+ WebFeature::kOverlayPopup,
WebFeature::kOverlayPopupAd,
WebFeature::kTrustTokenXhr,
WebFeature::kTrustTokenFetch,
@@ -169,6 +170,9 @@ UseCounterPageLoadMetricsObserver::GetAllowedUkmFeatures() {
WebFeature::kV8HTMLVideoElement_CancelVideoFrameCallback_Method,
WebFeature::kSchemefulSameSiteContextDowngrade,
WebFeature::kIdleDetectionStart,
+ WebFeature::kPerformanceObserverEntryTypesAndBuffered,
+ WebFeature::kStorageAccessAPI_HasStorageAccess_Method,
+ WebFeature::kStorageAccessAPI_requestStorageAccess_Method,
}));
return *opt_in_features;
}
diff --git a/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h
index 542c1dcec8e..d0aca784cea 100644
--- a/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h
@@ -7,7 +7,6 @@
#include <bitset>
#include "base/containers/flat_set.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
#include "third_party/blink/public/mojom/use_counter/css_property_id.mojom.h"
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc
index 2a31766142e..84b6e992068 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc
@@ -6,6 +6,7 @@
#include "base/timer/timer.h"
+#include "components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h"
#include "components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h"
#include "components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.h"
#include "components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h"
@@ -22,6 +23,8 @@ PageLoadMetricsEmbedderBase::~PageLoadMetricsEmbedderBase() = default;
void PageLoadMetricsEmbedderBase::RegisterObservers(PageLoadTracker* tracker) {
// Register observers used by all embedders
if (!IsPrerendering()) {
+ tracker->AddObserver(
+ std::make_unique<BackForwardCachePageLoadMetricsObserver>());
tracker->AddObserver(std::make_unique<CorePageLoadMetricsObserver>());
tracker->AddObserver(std::make_unique<LayoutPageLoadMetricsObserver>());
tracker->AddObserver(std::make_unique<UseCounterPageLoadMetricsObserver>());
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_observer.cc
index 758bae3025e..148a3b4b151 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_observer.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_observer.cc
@@ -112,35 +112,6 @@ bool PageLoadMetricsObserver::IsStandardWebPageMimeType(
return mime_type == "text/html" || mime_type == "application/xhtml+xml";
}
-// static
-bool PageLoadMetricsObserver::AssignTimeAndSizeForLargestContentfulPaint(
- const page_load_metrics::mojom::PaintTimingPtr& paint_timing,
- base::Optional<base::TimeDelta>* largest_content_paint_time,
- uint64_t* largest_content_paint_size,
- LargestContentType* largest_content_type) {
- base::Optional<base::TimeDelta>& text_time = paint_timing->largest_text_paint;
- base::Optional<base::TimeDelta>& image_time =
- paint_timing->largest_image_paint;
- uint64_t text_size = paint_timing->largest_text_paint_size;
- uint64_t image_size = paint_timing->largest_image_paint_size;
-
- // Size being 0 means the paint time is not recorded.
- if (!text_size && !image_size)
- return false;
-
- if ((text_size > image_size) ||
- (text_size == image_size && text_time < image_time)) {
- *largest_content_paint_time = text_time;
- *largest_content_paint_size = text_size;
- *largest_content_type = LargestContentType::kText;
- } else {
- *largest_content_paint_time = image_time;
- *largest_content_paint_size = image_size;
- *largest_content_type = LargestContentType::kImage;
- }
- return true;
-}
-
const PageLoadMetricsObserverDelegate& PageLoadMetricsObserver::GetDelegate()
const {
// The delegate must exist and outlive the page load metrics observer.
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/page_load_metrics_observer.h
index baecda6e2e8..7aab9839073 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_observer.h
@@ -195,10 +195,12 @@ class PageLoadMetricsObserver {
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
- enum class LargestContentType {
- kImage = 0,
- kText = 1,
- kMaxValue = kText,
+ enum class LargestContentState {
+ kReported = 0,
+ kLargestImageLoading = 1,
+ kNotFound = 2,
+ kFoundButNotReported = 3,
+ kMaxValue = kFoundButNotReported,
};
using FrameTreeNodeId = int;
@@ -207,13 +209,6 @@ class PageLoadMetricsObserver {
static bool IsStandardWebPageMimeType(const std::string& mime_type);
- // Returns true if the out parameters are assigned values.
- static bool AssignTimeAndSizeForLargestContentfulPaint(
- const page_load_metrics::mojom::PaintTimingPtr& paint_timing,
- base::Optional<base::TimeDelta>* largest_content_paint_time,
- uint64_t* largest_content_paint_size,
- LargestContentType* largest_content_type);
-
// Gets/Sets the delegate. The delegate must outlive the observer and is
// normally set when the observer is first registered for the page load. The
// delegate can only be set once.
@@ -369,6 +364,15 @@ class PageLoadMetricsObserver {
virtual void OnFirstContentfulPaintInPage(
const mojom::PageLoadTiming& timing) {}
+ // These are called once every time when the page is restored from the
+ // back-forward cache. |index| indicates |index|-th restore.
+ virtual void OnFirstPaintAfterBackForwardCacheRestoreInPage(
+ const mojom::BackForwardCacheTiming& timing,
+ size_t index) {}
+ virtual void OnFirstInputAfterBackForwardCacheRestoreInPage(
+ const mojom::BackForwardCacheTiming& timing,
+ size_t index) {}
+
// Unlike other paint callbacks, OnFirstMeaningfulPaintInMainFrameDocument is
// tracked per document, and is reported for the main frame document only.
virtual void OnFirstMeaningfulPaintInMainFrameDocument(
@@ -386,6 +390,9 @@ class PageLoadMetricsObserver {
content::RenderFrameHost* rfh,
const mojom::PageLoadFeatures& features) {}
+ virtual void OnThroughputUpdate(
+ const mojom::ThroughputUkmDataPtr& throughput_data) {}
+
// Invoked when there is data use for loading a resource on the page
// for a given render frame host. This only contains resources that have had
// new data use since the last callback. Resources loaded from the cache only
@@ -495,6 +502,10 @@ class PageLoadMetricsObserver {
// load.
virtual void OnEventOccurred(const void* const event_key) {}
+ // Called when the page tracked was just activated after being loaded inside a
+ // portal.
+ virtual void DidActivatePortal(base::TimeTicks activation_time) {}
+
private:
PageLoadMetricsObserverDelegate* delegate_ = nullptr;
};
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.cc
new file mode 100644
index 00000000000..6084579a962
--- /dev/null
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.cc
@@ -0,0 +1,16 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/page_load_metrics/browser/page_load_metrics_observer_delegate.h"
+
+namespace page_load_metrics {
+
+PageLoadMetricsObserverDelegate::BackForwardCacheRestore::
+ BackForwardCacheRestore(bool was_in_foreground)
+ : was_in_foreground(was_in_foreground) {}
+
+PageLoadMetricsObserverDelegate::BackForwardCacheRestore::
+ BackForwardCacheRestore(const BackForwardCacheRestore&) = default;
+
+} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h b/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h
index 00199f5f775..cec06c91ed6 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h
@@ -7,6 +7,7 @@
#include "base/optional.h"
#include "base/time/time.h"
+#include "components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h"
#include "components/page_load_metrics/browser/resource_tracker.h"
#include "components/page_load_metrics/common/page_end_reason.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
@@ -29,6 +30,20 @@ struct PageRenderData;
// from any PageLoadMetricsObserver.
class PageLoadMetricsObserverDelegate {
public:
+ // States when the page is restored from the back-forward cache.
+ struct BackForwardCacheRestore {
+ explicit BackForwardCacheRestore(bool was_in_foreground);
+ BackForwardCacheRestore(const BackForwardCacheRestore&);
+
+ // The first time when the page becomes backgrounded after the page is
+ // restored. The time is relative to the navigation start of bfcache restore
+ // avigation.
+ base::Optional<base::TimeDelta> first_background_time;
+
+ // True if the page was in foreground when the page is restored.
+ bool was_in_foreground = false;
+ };
+
virtual content::WebContents* GetWebContents() const = 0;
// The time the navigation was initiated.
@@ -42,6 +57,10 @@ class PageLoadMetricsObserverDelegate {
virtual const base::Optional<base::TimeDelta>& GetFirstForegroundTime()
const = 0;
+ // The state of index-th restore from the back-forward cache.
+ virtual const BackForwardCacheRestore& GetBackForwardCacheRestore(
+ size_t index) const = 0;
+
// True if the page load started in the foreground.
virtual bool StartedInForeground() const = 0;
@@ -104,6 +123,14 @@ class PageLoadMetricsObserverDelegate {
virtual const ui::ScopedVisibilityTracker& GetVisibilityTracker() const = 0;
virtual const ResourceTracker& GetResourceTracker() const = 0;
+ // Returns a shared LargestContentfulPaintHandler for page load metrics.
+ virtual const LargestContentfulPaintHandler&
+ GetLargestContentfulPaintHandler() const = 0;
+ // Returns a LargestContentfulPaintHandler for the experimental version of
+ // LCP.
+ virtual const LargestContentfulPaintHandler&
+ GetExperimentalLargestContentfulPaintHandler() const = 0;
+
// UKM SourceId for the current page load.
virtual ukm::SourceId GetSourceId() const = 0;
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc
index 6b858e895fb..c509c63db4d 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc
@@ -238,36 +238,58 @@ PageLoadMetricsTestWaiter::GetMatchedBits(
blink::LoadingBehaviorFlag::kLoadingBehaviorDocumentWriteBlockReload) {
matched_bits.Set(TimingField::kDocumentWriteBlockReload);
}
- if (timing.paint_timing->largest_image_paint ||
- timing.paint_timing->largest_text_paint) {
+ if (timing.paint_timing->largest_contentful_paint->largest_image_paint ||
+ timing.paint_timing->largest_contentful_paint->largest_text_paint) {
matched_bits.Set(TimingField::kLargestContentfulPaint);
}
if (timing.paint_timing->first_input_or_scroll_notified_timestamp)
matched_bits.Set(TimingField::kFirstInputOrScroll);
if (timing.interactive_timing->first_input_delay)
matched_bits.Set(TimingField::kFirstInputDelay);
+ if (!timing.back_forward_cache_timings.empty()) {
+ if (!timing.back_forward_cache_timings.back()
+ ->first_paint_after_back_forward_cache_restore.is_zero()) {
+ matched_bits.Set(TimingField::kFirstPaintAfterBackForwardCacheRestore);
+ }
+ if (timing.back_forward_cache_timings.back()
+ ->first_input_delay_after_back_forward_cache_restore.has_value()) {
+ matched_bits.Set(
+ TimingField::kFirstInputDelayAfterBackForwardCacheRestore);
+ }
+ }
return matched_bits;
}
void PageLoadMetricsTestWaiter::OnTrackerCreated(
page_load_metrics::PageLoadTracker* tracker) {
- if (!attach_on_tracker_creation_)
- return;
// A PageLoadMetricsWaiter should only wait for events from a single page
// load.
- ASSERT_FALSE(did_add_observer_);
- tracker->AddObserver(
- std::make_unique<WaiterMetricsObserver>(weak_factory_.GetWeakPtr()));
- did_add_observer_ = true;
+ if (!attach_on_tracker_creation_)
+ return;
+ AddObserver(tracker);
}
void PageLoadMetricsTestWaiter::OnCommit(
page_load_metrics::PageLoadTracker* tracker) {
+ // A PageLoadMetricsWaiter should only wait for events from a single page
+ // load.
if (attach_on_tracker_creation_)
return;
+ AddObserver(tracker);
+}
+
+void PageLoadMetricsTestWaiter::OnRestoredFromBackForwardCache(
+ page_load_metrics::PageLoadTracker* tracker) {
// A PageLoadMetricsWaiter should only wait for events from a single page
// load.
+ if (attach_on_tracker_creation_)
+ return;
+ AddObserver(tracker);
+}
+
+void PageLoadMetricsTestWaiter::AddObserver(
+ page_load_metrics::PageLoadTracker* tracker) {
ASSERT_FALSE(did_add_observer_);
tracker->AddObserver(
std::make_unique<WaiterMetricsObserver>(weak_factory_.GetWeakPtr()));
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.h b/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.h
index 6456c2643b9..cfff1e12a14 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.h
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.h
@@ -31,6 +31,8 @@ class PageLoadMetricsTestWaiter
kLargestContentfulPaint = 1 << 6,
kFirstInputOrScroll = 1 << 7,
kFirstInputDelay = 1 << 8,
+ kFirstPaintAfterBackForwardCacheRestore = 1 << 9,
+ kFirstInputDelayAfterBackForwardCacheRestore = 1 << 10,
};
using FrameTreeNodeId =
page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId;
@@ -228,6 +230,9 @@ class PageLoadMetricsTestWaiter
void OnCommit(page_load_metrics::PageLoadTracker* tracker) override;
+ void OnRestoredFromBackForwardCache(
+ page_load_metrics::PageLoadTracker* tracker) override;
+
bool CpuTimeExpectationsSatisfied() const;
bool ResourceUseExpectationsSatisfied() const;
@@ -238,6 +243,8 @@ class PageLoadMetricsTestWaiter
bool SubframeDataExpectationsSatisfied() const;
+ void AddObserver(page_load_metrics::PageLoadTracker* tracker);
+
std::unique_ptr<base::RunLoop> run_loop_;
TimingFieldBitSet page_expected_fields_;
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc
index d9f76dd0953..eb2dfaeaf1b 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc
@@ -194,6 +194,16 @@ internal::PageLoadTimingStatus IsValidPageLoadTiming(
return internal::INVALID_NULL_FIRST_INPUT_DELAY;
}
+ if (timing.interactive_timing->first_scroll_delay.has_value() &&
+ !timing.interactive_timing->first_scroll_timestamp.has_value()) {
+ return internal::INVALID_NULL_FIRST_SCROLL_TIMESTAMP;
+ }
+
+ if (!timing.interactive_timing->first_scroll_delay.has_value() &&
+ timing.interactive_timing->first_scroll_timestamp.has_value()) {
+ return internal::INVALID_NULL_FIRST_SCROLL_DELAY;
+ }
+
if (timing.interactive_timing->longest_input_delay.has_value() &&
!timing.interactive_timing->longest_input_timestamp.has_value()) {
return internal::INVALID_NULL_LONGEST_INPUT_TIMESTAMP;
@@ -252,6 +262,9 @@ class PageLoadTimingMerger {
MergeInteractiveTiming(navigation_start_offset,
*new_page_load_timing.interactive_timing,
is_main_frame);
+ MergeBackForwardCacheTiming(navigation_start_offset,
+ new_page_load_timing.back_forward_cache_timings,
+ is_main_frame);
}
// Whether we merged a new value.
@@ -327,16 +340,14 @@ class PageLoadTimingMerger {
target_paint_timing->first_meaningful_paint =
new_paint_timing.first_meaningful_paint;
- target_paint_timing->largest_image_paint =
- new_paint_timing.largest_image_paint;
- target_paint_timing->largest_image_paint_size =
- new_paint_timing.largest_image_paint_size;
- target_paint_timing->largest_text_paint =
- new_paint_timing.largest_text_paint;
- target_paint_timing->largest_text_paint_size =
- new_paint_timing.largest_text_paint_size;
+ target_paint_timing->largest_contentful_paint =
+ new_paint_timing.largest_contentful_paint->Clone();
+ target_paint_timing->experimental_largest_contentful_paint =
+ new_paint_timing.experimental_largest_contentful_paint.Clone();
target_paint_timing->first_input_or_scroll_notified_timestamp =
new_paint_timing.first_input_or_scroll_notified_timestamp;
+ target_paint_timing->portal_activated_paint =
+ new_paint_timing.portal_activated_paint;
}
}
@@ -354,6 +365,10 @@ class PageLoadTimingMerger {
// associated first input delay.
target_interactive_timing->first_input_delay =
new_interactive_timing.first_input_delay;
+ if (new_interactive_timing.first_input_processing_time.has_value()) {
+ target_interactive_timing->first_input_processing_time =
+ new_interactive_timing.first_input_processing_time;
+ }
}
if (new_interactive_timing.longest_input_delay.has_value()) {
@@ -369,6 +384,28 @@ class PageLoadTimingMerger {
new_longest_input_timestamp;
}
}
+
+ // Update First Scroll Delay.
+ if (MaybeUpdateTimeDelta(&target_interactive_timing->first_scroll_timestamp,
+ navigation_start_offset,
+ new_interactive_timing.first_scroll_timestamp)) {
+ target_interactive_timing->first_scroll_delay =
+ new_interactive_timing.first_scroll_delay;
+ }
+ }
+
+ void MergeBackForwardCacheTiming(
+ base::TimeDelta navigation_start_offset,
+ const std::vector<mojo::StructPtr<mojom::BackForwardCacheTiming>>&
+ new_back_forward_cache_timings,
+ bool is_main_frame) {
+ if (is_main_frame) {
+ target_->back_forward_cache_timings.clear();
+ target_->back_forward_cache_timings.reserve(
+ new_back_forward_cache_timings.size());
+ for (const auto& timing : new_back_forward_cache_timings)
+ target_->back_forward_cache_timings.push_back(timing.Clone());
+ }
}
// The target PageLoadTiming we are merging values into.
@@ -470,6 +507,18 @@ void PageLoadMetricsUpdateDispatcher::UpdateFeatures(
client_->UpdateFeaturesUsage(render_frame_host, new_features);
}
+void PageLoadMetricsUpdateDispatcher::UpdateThroughput(
+ content::RenderFrameHost* render_frame_host,
+ mojom::ThroughputUkmDataPtr throughput_data) {
+ if (embedder_interface_->IsExtensionUrl(
+ render_frame_host->GetLastCommittedURL())) {
+ // Extensions can inject child frames into a page. We don't want to track
+ // these as they could skew metrics. See http://crbug.com/761037
+ return;
+ }
+ client_->UpdateThroughput(std::move(throughput_data));
+}
+
void PageLoadMetricsUpdateDispatcher::DidFinishSubFrameNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->HasCommitted())
@@ -490,6 +539,12 @@ void PageLoadMetricsUpdateDispatcher::DidFinishSubFrameNavigation(
navigation_handle->GetFrameTreeNodeId(), navigation_delta));
}
+void PageLoadMetricsUpdateDispatcher::OnFrameDeleted(
+ content::RenderFrameHost* render_frame_host) {
+ subframe_navigation_start_offset_.erase(
+ render_frame_host->GetFrameTreeNodeId());
+}
+
void PageLoadMetricsUpdateDispatcher::UpdateSubFrameTiming(
content::RenderFrameHost* render_frame_host,
mojom::PageLoadTimingPtr new_timing) {
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h b/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h
index 0fbc534a82c..912c1bc27ab 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h
@@ -75,6 +75,11 @@ enum PageLoadTimingStatus {
// We received a longest input timestamp without a longest input delay.
INVALID_NULL_LONGEST_INPUT_DELAY,
+ // We received a first scroll delay without a first scroll timestamp.
+ INVALID_NULL_FIRST_SCROLL_TIMESTAMP,
+ // We received a first scroll timestamp without a first scroll delay.
+ INVALID_NULL_FIRST_SCROLL_DELAY,
+
// Longest input delay cannot happen before first input delay.
INVALID_LONGEST_INPUT_TIMESTAMP_LESS_THAN_FIRST_INPUT_TIMESTAMP,
@@ -127,6 +132,8 @@ class PageLoadMetricsUpdateDispatcher {
const mojom::FrameIntersectionUpdate& frame_intersection_update) = 0;
virtual void OnNewDeferredResourceCounts(
const mojom::DeferredResourceCounts& new_deferred_resource_data) = 0;
+ virtual void UpdateThroughput(
+ mojom::ThroughputUkmDataPtr throughput_data) = 0;
};
// The |client| instance must outlive this object.
@@ -147,6 +154,9 @@ class PageLoadMetricsUpdateDispatcher {
mojom::DeferredResourceCountsPtr new_deferred_resource_data,
mojom::InputTimingPtr input_timing_delta);
+ void UpdateThroughput(content::RenderFrameHost* render_frame_host,
+ mojom::ThroughputUkmDataPtr throughput_data);
+
// This method is only intended to be called for PageLoadFeatures being
// recorded directly from the browser process. Features coming from the
// renderer process should use the main flow into |UpdateMetrics|.
@@ -156,6 +166,8 @@ class PageLoadMetricsUpdateDispatcher {
void DidFinishSubFrameNavigation(
content::NavigationHandle* navigation_handle);
+ void OnFrameDeleted(content::RenderFrameHost* render_frame_host);
+
void ShutDown();
const mojom::PageLoadTiming& timing() const {
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_util.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_util.cc
index 9e33a8c8f13..5bcb45f80b3 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_util.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_util.cc
@@ -109,6 +109,19 @@ bool WasStartedInForegroundOptionalEventInForeground(
event.value() <= delegate.GetFirstBackgroundTime().value());
}
+bool WasStartedInForegroundOptionalEventInForegroundAfterBackForwardCacheRestore(
+ const base::Optional<base::TimeDelta>& event,
+ const PageLoadMetricsObserverDelegate& delegate,
+ size_t index) {
+ const auto& back_forward_cache_restore =
+ delegate.GetBackForwardCacheRestore(index);
+ base::Optional<base::TimeDelta> first_background_time =
+ back_forward_cache_restore.first_background_time;
+ return back_forward_cache_restore.was_in_foreground && event &&
+ (!first_background_time ||
+ event.value() <= first_background_time.value());
+}
+
bool WasStartedInBackgroundOptionalEventInForeground(
const base::Optional<base::TimeDelta>& event,
const PageLoadMetricsObserverDelegate& delegate) {
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_util.h b/chromium/components/page_load_metrics/browser/page_load_metrics_util.h
index bee6462ef5c..51cb8de9a77 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_util.h
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_util.h
@@ -103,6 +103,11 @@ bool WasStartedInForegroundOptionalEventInForeground(
const base::Optional<base::TimeDelta>& event,
const PageLoadMetricsObserverDelegate& delegate);
+bool WasStartedInForegroundOptionalEventInForegroundAfterBackForwardCacheRestore(
+ const base::Optional<base::TimeDelta>& event,
+ const PageLoadMetricsObserverDelegate& delegate,
+ size_t index);
+
// Returns true if:
// - We have timing information for the event.
// - The page load started in the background.
diff --git a/chromium/components/page_load_metrics/browser/page_load_tracker.cc b/chromium/components/page_load_metrics/browser/page_load_tracker.cc
index b9cd0830760..40a6f4844a2 100644
--- a/chromium/components/page_load_metrics/browser/page_load_tracker.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_tracker.cc
@@ -122,38 +122,82 @@ void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) {
completed_after_background);
}
+void DispatchEventsAfterBackForwardCacheRestore(
+ PageLoadMetricsObserver* observer,
+ const std::vector<mojo::StructPtr<mojom::BackForwardCacheTiming>>&
+ last_timings,
+ const std::vector<mojo::StructPtr<mojom::BackForwardCacheTiming>>&
+ new_timings) {
+ DCHECK_GE(new_timings.size(), last_timings.size());
+
+ for (size_t i = 0; i < new_timings.size(); i++) {
+ auto first_paint =
+ new_timings[i]->first_paint_after_back_forward_cache_restore;
+ if (!first_paint.is_zero() &&
+ (i >= last_timings.size() ||
+ last_timings[i]
+ ->first_paint_after_back_forward_cache_restore.is_zero())) {
+ observer->OnFirstPaintAfterBackForwardCacheRestoreInPage(*new_timings[i],
+ i);
+ }
+
+ auto first_input_delay =
+ new_timings[i]->first_input_delay_after_back_forward_cache_restore;
+ if (first_input_delay.has_value() &&
+ (i >= last_timings.size() ||
+ !last_timings[i]
+ ->first_input_delay_after_back_forward_cache_restore
+ .has_value())) {
+ observer->OnFirstInputAfterBackForwardCacheRestoreInPage(*new_timings[i],
+ i);
+ }
+ }
+}
+
void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer,
const mojom::PageLoadTiming& last_timing,
const mojom::PageLoadTiming& new_timing) {
if (!last_timing.Equals(new_timing))
observer->OnTimingUpdate(nullptr, new_timing);
if (new_timing.document_timing->dom_content_loaded_event_start &&
- !last_timing.document_timing->dom_content_loaded_event_start)
+ !last_timing.document_timing->dom_content_loaded_event_start) {
observer->OnDomContentLoadedEventStart(new_timing);
+ }
if (new_timing.document_timing->load_event_start &&
- !last_timing.document_timing->load_event_start)
+ !last_timing.document_timing->load_event_start) {
observer->OnLoadEventStart(new_timing);
+ }
if (new_timing.interactive_timing->first_input_delay &&
- !last_timing.interactive_timing->first_input_delay)
+ !last_timing.interactive_timing->first_input_delay) {
observer->OnFirstInputInPage(new_timing);
+ }
if (new_timing.paint_timing->first_paint &&
- !last_timing.paint_timing->first_paint)
+ !last_timing.paint_timing->first_paint) {
observer->OnFirstPaintInPage(new_timing);
+ }
+ DispatchEventsAfterBackForwardCacheRestore(
+ observer, last_timing.back_forward_cache_timings,
+ new_timing.back_forward_cache_timings);
if (new_timing.paint_timing->first_image_paint &&
- !last_timing.paint_timing->first_image_paint)
+ !last_timing.paint_timing->first_image_paint) {
observer->OnFirstImagePaintInPage(new_timing);
+ }
if (new_timing.paint_timing->first_contentful_paint &&
- !last_timing.paint_timing->first_contentful_paint)
+ !last_timing.paint_timing->first_contentful_paint) {
observer->OnFirstContentfulPaintInPage(new_timing);
+ }
if (new_timing.paint_timing->first_meaningful_paint &&
- !last_timing.paint_timing->first_meaningful_paint)
+ !last_timing.paint_timing->first_meaningful_paint) {
observer->OnFirstMeaningfulPaintInMainFrameDocument(new_timing);
+ }
if (new_timing.parse_timing->parse_start &&
- !last_timing.parse_timing->parse_start)
+ !last_timing.parse_timing->parse_start) {
observer->OnParseStart(new_timing);
+ }
if (new_timing.parse_timing->parse_stop &&
- !last_timing.parse_timing->parse_stop)
+ !last_timing.parse_timing->parse_stop) {
observer->OnParseStop(new_timing);
+ }
}
} // namespace
@@ -296,16 +340,31 @@ void PageLoadTracker::LogAbortChainHistograms(
void PageLoadTracker::PageHidden() {
// Only log the first time we background in a given page load.
- if (!first_background_time_.has_value()) {
+ if (!first_background_time_.has_value() ||
+ (!back_forward_cache_restores_.empty() &&
+ !back_forward_cache_restores_.back()
+ .first_background_time.has_value())) {
// Make sure we either started in the foreground and haven't been
// foregrounded yet, or started in the background and have already been
// foregrounded.
base::TimeTicks background_time;
- DCHECK_EQ(started_in_foreground_, !first_foreground_time_.has_value());
+
+ if (!first_background_time_.has_value())
+ DCHECK_EQ(started_in_foreground_, !first_foreground_time_.has_value());
+
background_time = base::TimeTicks::Now();
ClampBrowserTimestampIfInterProcessTimeTickSkew(&background_time);
DCHECK_GE(background_time, navigation_start_);
- first_background_time_ = background_time - navigation_start_;
+
+ if (!first_background_time_.has_value())
+ first_background_time_ = background_time - navigation_start_;
+
+ if (!back_forward_cache_restores_.empty() &&
+ !back_forward_cache_restores_.back()
+ .first_background_time.has_value()) {
+ back_forward_cache_restores_.back().first_background_time =
+ background_time - navigation_start_after_back_forward_cache_restore_;
+ }
}
visibility_tracker_.OnHidden();
INVOKE_AND_PRUNE_OBSERVERS(observers_, OnHidden,
@@ -331,6 +390,8 @@ void PageLoadTracker::PageShown() {
}
void PageLoadTracker::FrameDeleted(content::RenderFrameHost* rfh) {
+ metrics_update_dispatcher_.OnFrameDeleted(rfh);
+ largest_contentful_paint_handler_.OnFrameDeleted(rfh);
for (const auto& observer : observers_) {
observer->OnFrameDeleted(rfh);
}
@@ -349,6 +410,13 @@ void PageLoadTracker::Commit(content::NavigationHandle* navigation_handle) {
page_transition_ = navigation_handle->GetPageTransition();
user_initiated_info_.user_gesture = navigation_handle->HasUserGesture();
+ if (navigation_handle->IsInMainFrame()) {
+ largest_contentful_paint_handler_.RecordMainFrameTreeNodeId(
+ navigation_handle->GetFrameTreeNodeId());
+ experimental_largest_contentful_paint_handler_.RecordMainFrameTreeNodeId(
+ navigation_handle->GetFrameTreeNodeId());
+ }
+
const std::string& mime_type =
navigation_handle->GetWebContents()->GetContentsMimeType();
INVOKE_AND_PRUNE_OBSERVERS(observers_, ShouldObserveMimeType, mime_type);
@@ -380,6 +448,10 @@ void PageLoadTracker::ReadyToCommitNavigation(
void PageLoadTracker::DidFinishSubFrameNavigation(
content::NavigationHandle* navigation_handle) {
+ largest_contentful_paint_handler_.OnDidFinishSubFrameNavigation(
+ navigation_handle, navigation_start_);
+ experimental_largest_contentful_paint_handler_.OnDidFinishSubFrameNavigation(
+ navigation_handle, navigation_start_);
for (const auto& observer : observers_) {
observer->OnDidFinishSubFrameNavigation(navigation_handle);
}
@@ -632,6 +704,17 @@ void PageLoadTracker::OnTimingChanged() {
DCHECK(!last_dispatched_merged_page_timing_->Equals(
metrics_update_dispatcher_.timing()));
+ const mojom::PaintTimingPtr& paint_timing =
+ metrics_update_dispatcher_.timing().paint_timing;
+ largest_contentful_paint_handler_.RecordTiming(
+ *paint_timing->largest_contentful_paint,
+ paint_timing->first_input_or_scroll_notified_timestamp,
+ nullptr /* subframe_rfh */);
+ experimental_largest_contentful_paint_handler_.RecordTiming(
+ *paint_timing->experimental_largest_contentful_paint,
+ paint_timing->first_input_or_scroll_notified_timestamp,
+ nullptr /* subframe_rfh */);
+
for (const auto& observer : observers_) {
DispatchObserverTimingCallbacks(observer.get(),
*last_dispatched_merged_page_timing_,
@@ -645,6 +728,13 @@ void PageLoadTracker::OnSubFrameTimingChanged(
content::RenderFrameHost* rfh,
const mojom::PageLoadTiming& timing) {
DCHECK(rfh->GetParent());
+ const mojom::PaintTimingPtr& paint_timing = timing.paint_timing;
+ largest_contentful_paint_handler_.RecordTiming(
+ *paint_timing->largest_contentful_paint,
+ paint_timing->first_input_or_scroll_notified_timestamp, rfh);
+ experimental_largest_contentful_paint_handler_.RecordTiming(
+ *paint_timing->experimental_largest_contentful_paint,
+ paint_timing->first_input_or_scroll_notified_timestamp, rfh);
for (const auto& observer : observers_) {
observer->OnTimingUpdate(rfh, timing);
}
@@ -680,6 +770,11 @@ void PageLoadTracker::BroadcastEventToObservers(const void* const event_key) {
}
}
+void PageLoadTracker::DidActivatePortal(base::TimeTicks activation_time) {
+ for (const auto& observer : observers_)
+ observer->DidActivatePortal(activation_time);
+}
+
void PageLoadTracker::UpdateFeaturesUsage(
content::RenderFrameHost* rfh,
const mojom::PageLoadFeatures& new_features) {
@@ -688,6 +783,14 @@ void PageLoadTracker::UpdateFeaturesUsage(
}
}
+void PageLoadTracker::UpdateThroughput(
+ mojom::ThroughputUkmDataPtr throughput_data) {
+ if (!throughput_data)
+ return;
+ for (const auto& observer : observers_)
+ observer->OnThroughputUpdate(throughput_data);
+}
+
void PageLoadTracker::UpdateResourceDataUse(
content::RenderFrameHost* rfh,
const std::vector<mojom::ResourceDataUpdatePtr>& resources) {
@@ -738,6 +841,11 @@ const base::Optional<base::TimeDelta>& PageLoadTracker::GetFirstForegroundTime()
return first_foreground_time_;
}
+const PageLoadMetricsObserverDelegate::BackForwardCacheRestore&
+PageLoadTracker::GetBackForwardCacheRestore(size_t index) const {
+ return back_forward_cache_restores_[index];
+}
+
bool PageLoadTracker::StartedInForeground() const {
return started_in_foreground_;
}
@@ -808,6 +916,16 @@ const ResourceTracker& PageLoadTracker::GetResourceTracker() const {
return resource_tracker_;
}
+const LargestContentfulPaintHandler&
+PageLoadTracker::GetLargestContentfulPaintHandler() const {
+ return largest_contentful_paint_handler_;
+}
+
+const LargestContentfulPaintHandler&
+PageLoadTracker::GetExperimentalLargestContentfulPaintHandler() const {
+ return experimental_largest_contentful_paint_handler_;
+}
+
ukm::SourceId PageLoadTracker::GetSourceId() const {
return source_id_;
}
@@ -817,7 +935,6 @@ bool PageLoadTracker::IsFirstNavigationInWebContents() const {
}
void PageLoadTracker::OnEnterBackForwardCache() {
- DCHECK(visibility_tracker_.currently_in_foreground());
if (GetWebContents()->GetVisibility() == content::Visibility::VISIBLE) {
PageHidden();
}
@@ -826,11 +943,20 @@ void PageLoadTracker::OnEnterBackForwardCache() {
metrics_update_dispatcher_.timing());
}
-void PageLoadTracker::OnRestoreFromBackForwardCache() {
+void PageLoadTracker::OnRestoreFromBackForwardCache(
+ content::NavigationHandle* navigation_handle) {
+ navigation_start_after_back_forward_cache_restore_ =
+ navigation_handle->NavigationStart();
+
DCHECK(!visibility_tracker_.currently_in_foreground());
- if (GetWebContents()->GetVisibility() == content::Visibility::VISIBLE) {
+ bool visible =
+ GetWebContents()->GetVisibility() == content::Visibility::VISIBLE;
+
+ BackForwardCacheRestore back_forward_cache_restore(visible);
+ back_forward_cache_restores_.push_back(back_forward_cache_restore);
+
+ if (visible)
PageShown();
- }
for (const auto& observer : observers_) {
observer->OnRestoreFromBackForwardCache(
diff --git a/chromium/components/page_load_metrics/browser/page_load_tracker.h b/chromium/components/page_load_metrics/browser/page_load_tracker.h
index 6e8ab12c4db..ffa0f1973c7 100644
--- a/chromium/components/page_load_metrics/browser/page_load_tracker.h
+++ b/chromium/components/page_load_metrics/browser/page_load_tracker.h
@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "base/optional.h"
#include "base/time/time.h"
+#include "components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer_delegate.h"
#include "components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h"
@@ -203,14 +204,17 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
void OnFrameIntersectionUpdate(
content::RenderFrameHost* rfh,
const mojom::FrameIntersectionUpdate& frame_intersection_update) override;
+ void UpdateThroughput(mojom::ThroughputUkmDataPtr throughput_data) override;
- // PageLoadMetricsDelegate implementation:
+ // PageLoadMetricsObserverDelegate implementation:
content::WebContents* GetWebContents() const override;
base::TimeTicks GetNavigationStart() const override;
const base::Optional<base::TimeDelta>& GetFirstBackgroundTime()
const override;
const base::Optional<base::TimeDelta>& GetFirstForegroundTime()
const override;
+ const BackForwardCacheRestore& GetBackForwardCacheRestore(
+ size_t index) const override;
bool StartedInForeground() const override;
const UserInitiatedInfo& GetUserInitiatedInfo() const override;
const GURL& GetUrl() const override;
@@ -226,6 +230,10 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
const PageRenderData& GetMainFrameRenderData() const override;
const ui::ScopedVisibilityTracker& GetVisibilityTracker() const override;
const ResourceTracker& GetResourceTracker() const override;
+ const LargestContentfulPaintHandler& GetLargestContentfulPaintHandler()
+ const override;
+ const LargestContentfulPaintHandler&
+ GetExperimentalLargestContentfulPaintHandler() const override;
ukm::SourceId GetSourceId() const override;
bool IsFirstNavigationInWebContents() const override;
@@ -356,7 +364,12 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
void BroadcastEventToObservers(const void* const event_key);
void OnEnterBackForwardCache();
- void OnRestoreFromBackForwardCache();
+ void OnRestoreFromBackForwardCache(
+ content::NavigationHandle* navigation_handle);
+
+ // Called when the page tracked was just activated after being loaded inside a
+ // portal.
+ void DidActivatePortal(base::TimeTicks activation_time);
private:
// This function converts a TimeTicks value taken in the browser process
@@ -389,6 +402,10 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
// The navigation start in TimeTicks, not the wall time reported by Blink.
const base::TimeTicks navigation_start_;
+ // The navigation start after the last time when back-forward cache is
+ // restored.
+ base::TimeTicks navigation_start_after_back_forward_cache_restore_;
+
// The most recent URL of this page load. Updated at navigation start, upon
// redirection, and at commit time.
GURL url_;
@@ -424,7 +441,8 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
// when they occur in the background.
base::Optional<base::TimeDelta> first_background_time_;
base::Optional<base::TimeDelta> first_foreground_time_;
- bool started_in_foreground_;
+ std::vector<BackForwardCacheRestore> back_forward_cache_restores_;
+ const bool started_in_foreground_;
mojom::PageLoadTimingPtr last_dispatched_merged_page_timing_;
@@ -463,6 +481,11 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
const bool is_first_navigation_in_web_contents_;
+ page_load_metrics::LargestContentfulPaintHandler
+ largest_contentful_paint_handler_;
+ page_load_metrics::LargestContentfulPaintHandler
+ experimental_largest_contentful_paint_handler_;
+
DISALLOW_COPY_AND_ASSIGN(PageLoadTracker);
};
diff --git a/chromium/components/page_load_metrics/common/page_end_reason.h b/chromium/components/page_load_metrics/common/page_end_reason.h
index 94f3444267d..f6ab8eb2d36 100644
--- a/chromium/components/page_load_metrics/common/page_end_reason.h
+++ b/chromium/components/page_load_metrics/common/page_end_reason.h
@@ -15,7 +15,9 @@ namespace page_load_metrics {
// numeric values should never be reused. For any additions, also update the
// corresponding PageEndReason enum in enums.xml.
enum PageEndReason {
- // Page lifetime has not yet ended (page is still active).
+ // Page lifetime has not yet ended (page is still active). Pages that
+ // become hidden are logged in END_HIDDEN, so we expect low numbers in this
+ // bucket from the end of May 2020, i.e. in Navigation.PageEndReason2.
END_NONE = 0,
// The page was reloaded, possibly by the user.
@@ -50,6 +52,10 @@ enum PageEndReason {
// without committing, either without error or with net::ERR_ABORTED.
END_OTHER = 9,
+ // The page became hidden but is still active. Added end of May 2020 for
+ // Navigation.PageEndReason2.
+ END_HIDDEN = 10,
+
PAGE_END_REASON_COUNT
};
diff --git a/chromium/components/page_load_metrics/common/page_load_metrics.mojom b/chromium/components/page_load_metrics/common/page_load_metrics.mojom
index c40451d875a..f4c41890dd2 100644
--- a/chromium/components/page_load_metrics/common/page_load_metrics.mojom
+++ b/chromium/components/page_load_metrics/common/page_load_metrics.mojom
@@ -19,6 +19,22 @@ struct DocumentTiming {
mojo_base.mojom.TimeDelta? load_event_start;
};
+struct LargestContentfulPaintTiming {
+ // Time when the page's largest image is painted. Removed images are excluded.
+ mojo_base.mojom.TimeDelta? largest_image_paint;
+
+ // Size of the largest image of the largest image paint, by
+ // Size = Height * Width. Removed images are excluded.
+ uint64 largest_image_paint_size;
+
+ // Time when the page's largest text is painted. Removed text is excluded.
+ mojo_base.mojom.TimeDelta? largest_text_paint;
+
+ // Size of the largest text of the largest text paint, by
+ // Size = Height * Width. Removed text is excluded.
+ uint64 largest_text_paint_size;
+};
+
// TimeDeltas below relative to navigation start.
struct PaintTiming {
// Time when the first paint is performed.
@@ -33,23 +49,23 @@ struct PaintTiming {
// (Experimental) Time when the page's primary content is painted.
mojo_base.mojom.TimeDelta? first_meaningful_paint;
- // (Experimental) Time when the page's largest image is painted.
- mojo_base.mojom.TimeDelta? largest_image_paint;
+ // Largest contentful paint, which excludes removed content.
+ LargestContentfulPaintTiming largest_contentful_paint;
- // (Experimental) Size of the largest image of the largest image paint, by
- // Size = Height * Width.
- uint64 largest_image_paint_size;
+ // (Experimental) largest contentful paint including removed content.
+ LargestContentfulPaintTiming experimental_largest_contentful_paint;
- // (Experimental) Time when the page's largest text is painted.
- mojo_base.mojom.TimeDelta? largest_text_paint;
-
- // (Experimental) Size of the largest text of the largest text paint, by
- // Size = Height * Width.
- uint64 largest_text_paint_size;
+ // (Experimental) Time when the frame is first eligible to be painted, i.e.
+ // is first not render-throttled. Will be null if frame is throttled,
+ // unless there has already been a |first_paint|.
+ mojo_base.mojom.TimeDelta? first_eligible_to_paint;
// (Experimental) Time when first input or scroll is received, causing the
// largest contentful paint algorithm to stop.
mojo_base.mojom.TimeDelta? first_input_or_scroll_notified_timestamp;
+
+ // Time when the first paint happens after a portal activation.
+ mojo_base.mojom.TimeTicks? portal_activated_paint;
};
// TimeDeltas below represent durations of time during the page load.
@@ -105,6 +121,16 @@ struct InteractiveTiming {
// The timestamp of the event whose delay is reported as longest_input_delay.
mojo_base.mojom.TimeDelta? longest_input_timestamp;
+
+ // The latency between user input and display update for the first scroll after
+ // a navigation.
+ mojo_base.mojom.TimeDelta? first_scroll_delay;
+
+ // The timestamp of the user's first scroll after a navigation.
+ mojo_base.mojom.TimeDelta? first_scroll_timestamp;
+
+ // The duration of event handlers processing the first input event.
+ mojo_base.mojom.TimeDelta? first_input_processing_time;
};
// PageLoadTiming contains timing metrics associated with a page load. Many of
@@ -124,6 +150,11 @@ struct PageLoadTiming {
InteractiveTiming interactive_timing;
PaintTiming paint_timing;
ParseTiming parse_timing;
+
+ // List of back-forward cache timings, one for each time a page was restored
+ // from the cache.
+ array<BackForwardCacheTiming> back_forward_cache_timings;
+
// Time between user input and navigation start. This is set for navigations
// where the input start timing is known; currently when the navigation is
// initiated by a link click in the renderer, or from the desktop omnibox.
@@ -291,6 +322,21 @@ struct InputTiming {
uint64 num_input_events = 0;
};
+struct PercentOptional {
+ int8 percent;
+};
+
+// The throughput data for this page.
+struct ThroughputUkmData {
+ // The source id for the ukm.
+ int64 source_id;
+ // The throughput of the page of different threads: main, compositor, and the
+ // aggregated throughput of main && compositor thread.
+ int8 aggregated_throughput_percent;
+ int8 impl_throughput_percent;
+ PercentOptional? main_throughput_percent;
+};
+
// Sent from renderer to browser process when the PageLoadTiming for the
// associated frame changed.
interface PageLoadMetrics {
@@ -306,4 +352,24 @@ interface PageLoadMetrics {
CpuTiming cpu_load_timing,
DeferredResourceCounts new_deferred_resource_data,
InputTiming input_timing_delta);
+
+ // The renderer submit throughput data to the browser process. The browser
+ // process aggregates these data, and reports the aggregated value to UKM when
+ // the page shuts down or navigates away.
+ // TODO(xidachen): The interface exists for every frame, but this method is
+ // only called for LocalRoots. Should limit the visibility to LocalRoots only.
+ SubmitThroughputData(ThroughputUkmData throughput_data);
+};
+
+// TimeDelta below relative to the navigation start of the navigation restoring
+// page from the back- forward cache.
+struct BackForwardCacheTiming {
+ // Time when the first paint is performed after the time when the page
+ // is restored from the back-forward cache.
+ mojo_base.mojom.TimeDelta first_paint_after_back_forward_cache_restore;
+
+ // Queueing Time of the first click, tap, key press, cancellable touchstart,
+ // or pointer down followed by a pointer up after the time when the page is
+ // restored from the back-forward cache.
+ mojo_base.mojom.TimeDelta? first_input_delay_after_back_forward_cache_restore;
};
diff --git a/chromium/components/page_load_metrics/common/page_load_timing.cc b/chromium/components/page_load_metrics/common/page_load_timing.cc
index 6ec07e64bb9..09eae81d8e9 100644
--- a/chromium/components/page_load_metrics/common/page_load_timing.cc
+++ b/chromium/components/page_load_metrics/common/page_load_timing.cc
@@ -10,7 +10,13 @@ mojom::PageLoadTimingPtr CreatePageLoadTiming() {
return mojom::PageLoadTiming::New(
base::Time(), base::Optional<base::TimeDelta>(),
mojom::DocumentTiming::New(), mojom::InteractiveTiming::New(),
- mojom::PaintTiming::New(), mojom::ParseTiming::New(),
+ mojom::PaintTiming::New(base::nullopt, base::nullopt, base::nullopt,
+ base::nullopt,
+ mojom::LargestContentfulPaintTiming::New(),
+ mojom::LargestContentfulPaintTiming::New(),
+ base::nullopt, base::nullopt, base::nullopt),
+ mojom::ParseTiming::New(),
+ std::vector<mojo::StructPtr<mojom::BackForwardCacheTiming>>{},
base::Optional<base::TimeDelta>());
}
@@ -20,7 +26,9 @@ bool IsEmpty(const page_load_metrics::mojom::DocumentTiming& timing) {
bool IsEmpty(const page_load_metrics::mojom::InteractiveTiming& timing) {
return !timing.first_input_delay && !timing.first_input_timestamp &&
- !timing.longest_input_delay && !timing.longest_input_timestamp;
+ !timing.longest_input_delay && !timing.longest_input_timestamp &&
+ !timing.first_scroll_delay && !timing.first_scroll_timestamp &&
+ !timing.first_input_processing_time;
}
bool IsEmpty(const page_load_metrics::mojom::InputTiming& timing) {
@@ -32,7 +40,11 @@ bool IsEmpty(const page_load_metrics::mojom::InputTiming& timing) {
bool IsEmpty(const page_load_metrics::mojom::PaintTiming& timing) {
return !timing.first_paint && !timing.first_image_paint &&
!timing.first_contentful_paint && !timing.first_meaningful_paint &&
- !timing.largest_image_paint && !timing.largest_text_paint;
+ !timing.largest_contentful_paint->largest_image_paint &&
+ !timing.largest_contentful_paint->largest_text_paint &&
+ !timing.experimental_largest_contentful_paint->largest_image_paint &&
+ !timing.experimental_largest_contentful_paint->largest_text_paint &&
+ !timing.first_eligible_to_paint;
}
bool IsEmpty(const page_load_metrics::mojom::ParseTiming& timing) {
@@ -52,14 +64,20 @@ bool IsEmpty(const page_load_metrics::mojom::PageLoadTiming& timing) {
(!timing.paint_timing ||
page_load_metrics::IsEmpty(*timing.paint_timing)) &&
(!timing.parse_timing ||
- page_load_metrics::IsEmpty(*timing.parse_timing));
+ page_load_metrics::IsEmpty(*timing.parse_timing)) &&
+ timing.back_forward_cache_timings.empty();
}
void InitPageLoadTimingForTest(mojom::PageLoadTiming* timing) {
timing->document_timing = mojom::DocumentTiming::New();
timing->interactive_timing = mojom::InteractiveTiming::New();
timing->paint_timing = mojom::PaintTiming::New();
+ timing->paint_timing->largest_contentful_paint =
+ mojom::LargestContentfulPaintTiming::New();
+ timing->paint_timing->experimental_largest_contentful_paint =
+ mojom::LargestContentfulPaintTiming::New();
timing->parse_timing = mojom::ParseTiming::New();
+ timing->back_forward_cache_timings.clear();
}
} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.cc b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
index f0d615cfac0..da17af31f96 100644
--- a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
+++ b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
@@ -39,12 +39,16 @@ base::TimeTicks ClampToStart(base::TimeTicks event, base::TimeTicks start) {
class MojoPageTimingSender : public PageTimingSender {
public:
- explicit MojoPageTimingSender(content::RenderFrame* render_frame) {
+ explicit MojoPageTimingSender(content::RenderFrame* render_frame,
+ bool limited_sending_mode)
+ : limited_sending_mode_(limited_sending_mode) {
DCHECK(render_frame);
render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
&page_load_metrics_);
}
+
~MojoPageTimingSender() override = default;
+
void SendTiming(const mojom::PageLoadTimingPtr& timing,
const mojom::FrameMetadataPtr& metadata,
mojom::PageLoadFeaturesPtr new_features,
@@ -55,12 +59,34 @@ class MojoPageTimingSender : public PageTimingSender {
mojom::InputTimingPtr input_timing_delta) override {
DCHECK(page_load_metrics_);
page_load_metrics_->UpdateTiming(
- timing->Clone(), metadata->Clone(), std::move(new_features),
- std::move(resources), render_data.Clone(), cpu_timing->Clone(),
+ limited_sending_mode_ ? CreatePageLoadTiming() : timing->Clone(),
+ metadata->Clone(), std::move(new_features), std::move(resources),
+ render_data.Clone(), cpu_timing->Clone(),
std::move(new_deferred_resource_data), std::move(input_timing_delta));
}
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {
+ DCHECK(page_load_metrics_);
+ mojom::PercentOptionalPtr main_ptr =
+ main_percent.has_value()
+ ? mojom::PercentOptional::New(main_percent.value())
+ : nullptr;
+ mojom::ThroughputUkmDataPtr throughput_data = mojom::ThroughputUkmData::New(
+ source_id, aggregated_percent, impl_percent, std::move(main_ptr));
+ page_load_metrics_->SubmitThroughputData(std::move(throughput_data));
+ }
+
private:
+ // Indicates that this sender should not send timing updates or frame render
+ // data updates.
+ // TODO(https://crbug.com/1097127): When timing updates are handled for cases
+ // where we have a subframe document and no committed navigation, this can be
+ // removed.
+ bool limited_sending_mode_ = false;
+
// Use associated interface to make sure mojo messages are ordered with regard
// to legacy IPC messages.
mojo::AssociatedRemote<mojom::PageLoadMetrics> page_load_metrics_;
@@ -246,17 +272,49 @@ void MetricsRenderFrameObserver::DidFailProvisionalLoad() {
provisional_frame_resource_data_use_.reset();
}
-void MetricsRenderFrameObserver::DidCommitProvisionalLoad(
- bool is_same_document_navigation,
- ui::PageTransition transition) {
- // Same-document navigations (e.g. a navigation from a fragment link) aren't
- // full page loads, since they don't go to network to load the main HTML
- // resource. DidStartProvisionalLoad doesn't get invoked for same document
- // navigations, so we may still have an active page_timing_metrics_sender_ at
- // this point.
- if (is_same_document_navigation)
+void MetricsRenderFrameObserver::DidCreateDocumentElement() {
+ // If we do not have a render frame or are already tracking this frame, ignore
+ // the new document element.
+ if (HasNoRenderFrame() || page_timing_metrics_sender_)
+ return;
+
+ // We should only track committed navigations for the main frame so ignore new
+ // document elements in the main frame.
+ if (render_frame()->IsMainFrame())
return;
+ // Every frame creates an initial about:blank document element prior to
+ // receiving a navigation to about:blank. Ignore this initial document
+ // element.
+ if (!first_document_observed_) {
+ first_document_observed_ = true;
+ return;
+ }
+
+ // A new document element was created in a frame that did not commit a
+ // provisional load. This can be due to a doc.write in the frame that aborted
+ // a navigation. Create a page timing sender to track this load. This sender
+ // will only send resource usage updates to the browser process. There
+ // currently is not infrastructure in the browser process to monitor this case
+ // and properly handle timing updates without a committed load.
+ // TODO(https://crbug.com/1097127): Implement proper handling of timing
+ // updates in the browser process and create a normal page timing sender.
+
+ // It should not be possible to have a |provisional_frame_resource_data_use_|
+ // object at this point. If we did, it means we reached
+ // ReadyToCommitNavigation() and aborted prior to load commit which should not
+ // be possible.
+ DCHECK(!provisional_frame_resource_data_use_);
+
+ Timing timing = GetTiming();
+ page_timing_metrics_sender_ = std::make_unique<PageTimingMetricsSender>(
+ CreatePageTimingSender(true /* limited_sending_mode */), CreateTimer(),
+ std::move(timing.relative_timing), timing.monotonic_timing,
+ std::make_unique<PageResourceDataUse>());
+}
+
+void MetricsRenderFrameObserver::DidCommitProvisionalLoad(
+ ui::PageTransition transition) {
// Make sure to release the sender for a previous navigation, if we have one.
page_timing_metrics_sender_.reset();
@@ -274,7 +332,7 @@ void MetricsRenderFrameObserver::DidCommitProvisionalLoad(
Timing timing = GetTiming();
page_timing_metrics_sender_ = std::make_unique<PageTimingMetricsSender>(
- CreatePageTimingSender(), CreateTimer(),
+ CreatePageTimingSender(false /* limited_sending_mode*/), CreateTimer(),
std::move(timing.relative_timing), timing.monotonic_timing,
std::move(provisional_frame_resource_data_use_));
}
@@ -301,6 +359,18 @@ void MetricsRenderFrameObserver::OnMainFrameDocumentIntersectionChanged(
main_frame_document_intersection);
}
+void MetricsRenderFrameObserver::OnThroughputDataAvailable(
+ ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {
+ std::unique_ptr<MojoPageTimingSender> sender =
+ std::make_unique<MojoPageTimingSender>(render_frame(),
+ false /* limited_sending_mode */);
+ sender->SubmitThroughputData(source_id, aggregated_percent, impl_percent,
+ main_percent);
+}
+
void MetricsRenderFrameObserver::MaybeSetCompletedBeforeFCP(int request_id) {
if (HasNoRenderFrame())
return;
@@ -410,6 +480,17 @@ MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
timing->interactive_timing->longest_input_timestamp =
ClampDelta((*perf.LongestInputTimestamp()).InSecondsF(), start);
}
+ if (perf.FirstInputProcessingTime().has_value()) {
+ timing->interactive_timing->first_input_processing_time =
+ *perf.FirstInputProcessingTime();
+ }
+ if (perf.FirstScrollDelay().has_value()) {
+ timing->interactive_timing->first_scroll_delay = *perf.FirstScrollDelay();
+ }
+ if (perf.FirstScrollTimestamp().has_value()) {
+ timing->interactive_timing->first_scroll_timestamp =
+ ClampDelta((*perf.FirstScrollTimestamp()).InSecondsF(), start);
+ }
if (perf.ResponseStart() > 0.0)
timing->response_start = ClampDelta(perf.ResponseStart(), start);
if (perf.DomContentLoadedEventStart() > 0.0) {
@@ -422,11 +503,36 @@ MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
}
if (perf.FirstPaint() > 0.0)
timing->paint_timing->first_paint = ClampDelta(perf.FirstPaint(), start);
+ if (!perf.BackForwardCacheRestore().empty()) {
+ blink::WebPerformance::BackForwardCacheRestoreTimings restore_timings =
+ perf.BackForwardCacheRestore();
+ for (const auto& restore_timing : restore_timings) {
+ double navigation_start = restore_timing.navigation_start;
+ double first_paint = restore_timing.first_paint;
+ base::Optional<base::TimeDelta> first_input_delay =
+ restore_timing.first_input_delay;
+
+ auto back_forward_cache_timing = mojom::BackForwardCacheTiming::New();
+ if (first_paint) {
+ back_forward_cache_timing
+ ->first_paint_after_back_forward_cache_restore =
+ ClampDelta(first_paint, navigation_start);
+ }
+ if (first_input_delay.has_value()) {
+ back_forward_cache_timing
+ ->first_input_delay_after_back_forward_cache_restore =
+ ClampDelta(first_input_delay->InSecondsF(), navigation_start);
+ }
+ timing->back_forward_cache_timings.push_back(
+ std::move(back_forward_cache_timing));
+ }
+ }
if (perf.FirstImagePaint() > 0.0) {
timing->paint_timing->first_image_paint =
ClampDelta(perf.FirstImagePaint(), start);
}
if (perf.FirstContentfulPaint() > 0.0) {
+ DCHECK(perf.FirstEligibleToPaint() > 0);
timing->paint_timing->first_contentful_paint =
ClampDelta(perf.FirstContentfulPaint(), start);
monotonic_timing.first_contentful_paint =
@@ -438,13 +544,13 @@ MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
ClampDelta(perf.FirstMeaningfulPaint(), start);
}
if (perf.LargestImagePaintSize() > 0) {
- timing->paint_timing->largest_image_paint_size =
+ timing->paint_timing->largest_contentful_paint->largest_image_paint_size =
perf.LargestImagePaintSize();
// Note that size can be nonzero while the time is 0 since a time of 0 is
// sent when the image is painting. We assign the time even when it is 0 so
// that it's not ignored, but need to be careful when doing operations on
// the value.
- timing->paint_timing->largest_image_paint =
+ timing->paint_timing->largest_contentful_paint->largest_image_paint =
perf.LargestImagePaint() == 0.0
? base::TimeDelta()
: ClampDelta(perf.LargestImagePaint(), start);
@@ -453,9 +559,43 @@ MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
// LargestTextPaint and LargestTextPaintSize should be available at the
// same time. This is a renderer side DCHECK to ensure this.
DCHECK(perf.LargestTextPaint());
- timing->paint_timing->largest_text_paint =
+ timing->paint_timing->largest_contentful_paint->largest_text_paint =
ClampDelta(perf.LargestTextPaint(), start);
- timing->paint_timing->largest_text_paint_size = perf.LargestTextPaintSize();
+ timing->paint_timing->largest_contentful_paint->largest_text_paint_size =
+ perf.LargestTextPaintSize();
+ }
+ if (perf.ExperimentalLargestImagePaintSize() > 0) {
+ timing->paint_timing->experimental_largest_contentful_paint
+ ->largest_image_paint_size = perf.ExperimentalLargestImagePaintSize();
+ // Note that size can be nonzero while the time is 0 since a time of 0 is
+ // sent when the image is painting. We assign the time even when it is 0 so
+ // that it's not ignored, but need to be careful when doing operations on
+ // the value.
+ timing->paint_timing->experimental_largest_contentful_paint
+ ->largest_image_paint =
+ perf.ExperimentalLargestImagePaint() == 0.0
+ ? base::TimeDelta()
+ : ClampDelta(perf.ExperimentalLargestImagePaint(), start);
+ }
+ if (perf.ExperimentalLargestTextPaintSize() > 0) {
+ // ExperimentalLargestTextPaint and ExperimentalLargestTextPaintSize should
+ // be available at the same time. This is a renderer side DCHECK to ensure
+ // this.
+ DCHECK(perf.ExperimentalLargestTextPaint());
+ timing->paint_timing->experimental_largest_contentful_paint
+ ->largest_text_paint =
+ ClampDelta(perf.ExperimentalLargestTextPaint(), start);
+ timing->paint_timing->experimental_largest_contentful_paint
+ ->largest_text_paint_size = perf.ExperimentalLargestTextPaintSize();
+ }
+ // It is possible for a frame to switch from eligible for painting to
+ // ineligible for it prior to the first paint. If this occurs, we need to
+ // propagate the null value.
+ if (perf.FirstEligibleToPaint() > 0) {
+ timing->paint_timing->first_eligible_to_paint =
+ ClampDelta(perf.FirstEligibleToPaint(), start);
+ } else {
+ timing->paint_timing->first_eligible_to_paint.reset();
}
if (perf.FirstInputOrScrollNotifiedTimestamp() > 0) {
timing->paint_timing->first_input_or_scroll_notified_timestamp =
@@ -482,6 +622,10 @@ MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
base::TimeDelta::FromSecondsD(
perf.ParseBlockedOnScriptExecutionFromDocumentWriteDuration());
}
+ if (perf.LastPortalActivatedPaint().has_value()) {
+ timing->paint_timing->portal_activated_paint =
+ *perf.LastPortalActivatedPaint();
+ }
return Timing(std::move(timing), monotonic_timing);
}
@@ -491,9 +635,9 @@ std::unique_ptr<base::OneShotTimer> MetricsRenderFrameObserver::CreateTimer() {
}
std::unique_ptr<PageTimingSender>
-MetricsRenderFrameObserver::CreatePageTimingSender() {
+MetricsRenderFrameObserver::CreatePageTimingSender(bool limited_sending_mode) {
return base::WrapUnique<PageTimingSender>(
- new MojoPageTimingSender(render_frame()));
+ new MojoPageTimingSender(render_frame(), limited_sending_mode));
}
bool MetricsRenderFrameObserver::HasNoRenderFrame() const {
diff --git a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.h b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.h
index 9dc048cb1a4..c37e1cf91f9 100644
--- a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.h
+++ b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.h
@@ -77,8 +77,8 @@ class MetricsRenderFrameObserver
void ReadyToCommitNavigation(
blink::WebDocumentLoader* document_loader) override;
void DidFailProvisionalLoad() override;
- void DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition transition) override;
+ void DidCommitProvisionalLoad(ui::PageTransition transition) override;
+ void DidCreateDocumentElement() override;
void OnDestruct() override;
// Invoked when a frame is going away. This is our last chance to send IPCs
@@ -96,6 +96,11 @@ class MetricsRenderFrameObserver
void OnMainFrameDocumentIntersectionChanged(
const blink::WebRect& main_frame_document_intersection) override;
+ void OnThroughputDataAvailable(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) override;
+
protected:
// The relative and monotonic page load timings.
struct Timing {
@@ -125,9 +130,14 @@ class MetricsRenderFrameObserver
void SendMetrics();
virtual Timing GetTiming() const;
virtual std::unique_ptr<base::OneShotTimer> CreateTimer();
- virtual std::unique_ptr<PageTimingSender> CreatePageTimingSender();
+ virtual std::unique_ptr<PageTimingSender> CreatePageTimingSender(
+ bool limited_sending_mode);
virtual bool HasNoRenderFrame() const;
+ // Whether the initial about:blank document loaded into every frame was
+ // observed.
+ bool first_document_observed_ = false;
+
// Collects the data use of the frame request for a provisional load until the
// load is committed. We want to collect data use for completed navigations in
// this class, but the various navigation callbacks do not provide enough data
diff --git a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer_unittest.cc b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer_unittest.cc
index 6bc88c9ef83..a4789646bb0 100644
--- a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer_unittest.cc
+++ b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer_unittest.cc
@@ -32,7 +32,8 @@ class TestMetricsRenderFrameObserver : public MetricsRenderFrameObserver,
return std::move(timer);
}
- std::unique_ptr<PageTimingSender> CreatePageTimingSender() override {
+ std::unique_ptr<PageTimingSender> CreatePageTimingSender(
+ bool limited_sending_mode) override {
return base::WrapUnique<PageTimingSender>(
new FakePageTimingSender(&validator_));
}
@@ -88,7 +89,7 @@ TEST_F(MetricsRenderFrameObserverTest, SingleMetric) {
observer.ExpectPageLoadTiming(timing);
observer.DidStartNavigation(GURL(), base::nullopt);
observer.ReadyToCommitNavigation(nullptr);
- observer.DidCommitProvisionalLoad(false, ui::PAGE_TRANSITION_LINK);
+ observer.DidCommitProvisionalLoad(ui::PAGE_TRANSITION_LINK);
observer.GetMockTimer()->Fire();
timing.parse_timing->parse_start = base::TimeDelta::FromMilliseconds(10);
@@ -111,7 +112,7 @@ TEST_F(MetricsRenderFrameObserverTest, SingleCpuMetric) {
observer.ExpectPageLoadTiming(timing);
observer.DidStartNavigation(GURL(), base::nullopt);
observer.ReadyToCommitNavigation(nullptr);
- observer.DidCommitProvisionalLoad(false, ui::PAGE_TRANSITION_LINK);
+ observer.DidCommitProvisionalLoad(ui::PAGE_TRANSITION_LINK);
// Send cpu timing updates and verify the expected result.
observer.DidChangeCpuTiming(base::TimeDelta::FromMilliseconds(110));
@@ -133,7 +134,7 @@ TEST_F(MetricsRenderFrameObserverTest, MultipleMetrics) {
observer.ExpectPageLoadTiming(timing);
observer.DidStartNavigation(GURL(), base::nullopt);
observer.ReadyToCommitNavigation(nullptr);
- observer.DidCommitProvisionalLoad(false, ui::PAGE_TRANSITION_LINK);
+ observer.DidCommitProvisionalLoad(ui::PAGE_TRANSITION_LINK);
observer.GetMockTimer()->Fire();
timing.document_timing->dom_content_loaded_event_start = dom_event;
@@ -179,7 +180,7 @@ TEST_F(MetricsRenderFrameObserverTest, MultipleNavigations) {
observer.ExpectPageLoadTiming(timing);
observer.DidStartNavigation(GURL(), base::nullopt);
observer.ReadyToCommitNavigation(nullptr);
- observer.DidCommitProvisionalLoad(false, ui::PAGE_TRANSITION_LINK);
+ observer.DidCommitProvisionalLoad(ui::PAGE_TRANSITION_LINK);
observer.GetMockTimer()->Fire();
timing.document_timing->dom_content_loaded_event_start = dom_event;
@@ -205,7 +206,7 @@ TEST_F(MetricsRenderFrameObserverTest, MultipleNavigations) {
observer.ExpectPageLoadTiming(timing_2);
observer.DidStartNavigation(GURL(), base::nullopt);
observer.ReadyToCommitNavigation(nullptr);
- observer.DidCommitProvisionalLoad(false, ui::PAGE_TRANSITION_LINK);
+ observer.DidCommitProvisionalLoad(ui::PAGE_TRANSITION_LINK);
observer.GetMockTimer()->Fire();
timing_2.document_timing->dom_content_loaded_event_start = dom_event_2;
diff --git a/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.cc b/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.cc
index 36625df9f45..3cd64fefc99 100644
--- a/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.cc
+++ b/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.cc
@@ -44,9 +44,9 @@ PageTimingMetricsSender::PageTimingMetricsSender(
new_deferred_resource_data_(mojom::DeferredResourceCounts::New()),
buffer_timer_delay_ms_(kBufferTimerDelayMillis),
metadata_recorder_(initial_monotonic_timing) {
+ const auto resource_id = initial_request->resource_id();
page_resource_data_use_.emplace(
- std::piecewise_construct,
- std::forward_as_tuple(initial_request->resource_id()),
+ std::piecewise_construct, std::forward_as_tuple(resource_id),
std::forward_as_tuple(std::move(initial_request)));
buffer_timer_delay_ms_ = base::GetFieldTrialParamByFeatureAsInt(
kPageLoadMetricsTimerDelayFeature, "BufferTimerDelayMillis",