summaryrefslogtreecommitdiff
path: root/chromium/components/page_load_metrics/renderer
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/page_load_metrics/renderer')
-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
4 files changed, 185 insertions, 30 deletions
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",