123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729 |
- // Copyright 2015 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/renderer/metrics_render_frame_observer.h"
- #include <string>
- #include <utility>
- #include "base/memory/ptr_util.h"
- #include "base/time/time.h"
- #include "base/timer/timer.h"
- #include "components/page_load_metrics/renderer/page_timing_metrics_sender.h"
- #include "components/page_load_metrics/renderer/page_timing_sender.h"
- #include "content/public/renderer/render_frame.h"
- #include "services/network/public/mojom/url_response_head.mojom.h"
- #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
- #include "third_party/blink/public/common/loader/resource_type_util.h"
- #include "third_party/blink/public/common/mobile_metrics/mobile_friendliness.h"
- #include "third_party/blink/public/web/web_document.h"
- #include "third_party/blink/public/web/web_document_loader.h"
- #include "third_party/blink/public/web/web_local_frame.h"
- #include "third_party/blink/public/web/web_performance.h"
- #include "url/gurl.h"
- namespace page_load_metrics {
- namespace {
- base::TimeDelta ClampDelta(double event, double start) {
- if (event - start < 0)
- event = start;
- return base::Time::FromDoubleT(event) - base::Time::FromDoubleT(start);
- }
- base::TimeTicks ClampToStart(base::TimeTicks event, base::TimeTicks start) {
- return event < start ? start : event;
- }
- class MojoPageTimingSender : public PageTimingSender {
- public:
- 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,
- const std::vector<blink::UseCounterFeature>& new_features,
- std::vector<mojom::ResourceDataUpdatePtr> resources,
- const mojom::FrameRenderDataUpdate& render_data,
- const mojom::CpuTimingPtr& cpu_timing,
- mojom::InputTimingPtr input_timing_delta,
- const absl::optional<blink::MobileFriendliness>& mobile_friendliness,
- uint32_t soft_navigation_count) override {
- DCHECK(page_load_metrics_);
- page_load_metrics_->UpdateTiming(
- limited_sending_mode_ ? CreatePageLoadTiming() : timing->Clone(),
- metadata->Clone(), new_features, std::move(resources),
- render_data.Clone(), cpu_timing->Clone(), std::move(input_timing_delta),
- std::move(mobile_friendliness), soft_navigation_count);
- }
- void SetUpSmoothnessReporting(
- base::ReadOnlySharedMemoryRegion shared_memory) override {
- DCHECK(page_load_metrics_);
- page_load_metrics_->SetUpSharedMemoryForSmoothness(
- std::move(shared_memory));
- }
- 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_;
- };
- } // namespace
- MetricsRenderFrameObserver::MetricsRenderFrameObserver(
- content::RenderFrame* render_frame)
- : content::RenderFrameObserver(render_frame) {}
- MetricsRenderFrameObserver::~MetricsRenderFrameObserver() {
- if (page_timing_metrics_sender_)
- page_timing_metrics_sender_->SendLatest();
- }
- void MetricsRenderFrameObserver::DidChangePerformanceTiming() {
- SendMetrics();
- }
- void MetricsRenderFrameObserver::DidObserveInputDelay(
- base::TimeDelta input_delay) {
- if (!page_timing_metrics_sender_ || HasNoRenderFrame()) {
- return;
- }
- page_timing_metrics_sender_->DidObserveInputDelay(input_delay);
- }
- void MetricsRenderFrameObserver::DidObserveUserInteraction(
- base::TimeDelta max_event_duration,
- blink::UserInteractionType interaction_type) {
- if (!page_timing_metrics_sender_ || HasNoRenderFrame()) {
- return;
- }
- page_timing_metrics_sender_->DidObserveUserInteraction(max_event_duration,
- interaction_type);
- }
- void MetricsRenderFrameObserver::DidChangeCpuTiming(base::TimeDelta time) {
- if (!page_timing_metrics_sender_)
- return;
- if (HasNoRenderFrame())
- return;
- page_timing_metrics_sender_->UpdateCpuTiming(time);
- }
- void MetricsRenderFrameObserver::DidObserveLoadingBehavior(
- blink::LoadingBehaviorFlag behavior) {
- if (page_timing_metrics_sender_)
- page_timing_metrics_sender_->DidObserveLoadingBehavior(behavior);
- }
- void MetricsRenderFrameObserver::DidObserveNewFeatureUsage(
- const blink::UseCounterFeature& feature) {
- if (page_timing_metrics_sender_)
- page_timing_metrics_sender_->DidObserveNewFeatureUsage(feature);
- }
- void MetricsRenderFrameObserver::DidObserveSoftNavigation(uint32_t count) {
- if (page_timing_metrics_sender_) {
- page_timing_metrics_sender_->DidObserveSoftNavigation(count);
- }
- }
- void MetricsRenderFrameObserver::DidObserveLayoutShift(
- double score,
- bool after_input_or_scroll) {
- if (page_timing_metrics_sender_)
- page_timing_metrics_sender_->DidObserveLayoutShift(score,
- after_input_or_scroll);
- }
- void MetricsRenderFrameObserver::DidObserveLayoutNg(uint32_t all_block_count,
- uint32_t ng_block_count,
- uint32_t all_call_count,
- uint32_t ng_call_count) {
- if (page_timing_metrics_sender_)
- page_timing_metrics_sender_->DidObserveLayoutNg(
- all_block_count, ng_block_count, all_call_count, ng_call_count);
- }
- void MetricsRenderFrameObserver::DidStartResponse(
- const url::SchemeHostPort& final_response_url,
- int request_id,
- const network::mojom::URLResponseHead& response_head,
- network::mojom::RequestDestination request_destination) {
- if (provisional_frame_resource_data_use_ &&
- blink::IsRequestDestinationFrame(request_destination)) {
- // TODO(rajendrant): This frame request might start before the provisional
- // load starts, and data use of the frame request might be missed in that
- // case. There should be a guarantee that DidStartProvisionalLoad be called
- // before DidStartResponse for the frame request.
- provisional_frame_resource_data_use_->DidStartResponse(
- final_response_url, request_id, response_head, request_destination);
- } else if (page_timing_metrics_sender_) {
- page_timing_metrics_sender_->DidStartResponse(
- final_response_url, request_id, response_head, request_destination);
- UpdateResourceMetadata(request_id);
- }
- }
- void MetricsRenderFrameObserver::DidCompleteResponse(
- int request_id,
- const network::URLLoaderCompletionStatus& status) {
- if (provisional_frame_resource_data_use_ &&
- provisional_frame_resource_data_use_->resource_id() == request_id) {
- provisional_frame_resource_data_use_->DidCompleteResponse(status);
- } else if (page_timing_metrics_sender_) {
- page_timing_metrics_sender_->DidCompleteResponse(request_id, status);
- MaybeSetCompletedBeforeFCP(request_id);
- UpdateResourceMetadata(request_id);
- }
- }
- void MetricsRenderFrameObserver::DidCancelResponse(int request_id) {
- if (provisional_frame_resource_data_use_ &&
- provisional_frame_resource_data_use_->resource_id() == request_id) {
- provisional_frame_resource_data_use_.reset();
- } else if (page_timing_metrics_sender_) {
- page_timing_metrics_sender_->DidCancelResponse(request_id);
- UpdateResourceMetadata(request_id);
- }
- }
- void MetricsRenderFrameObserver::DidReceiveTransferSizeUpdate(
- int request_id,
- int received_data_length) {
- if (provisional_frame_resource_data_use_ &&
- provisional_frame_resource_data_use_->resource_id() == request_id) {
- provisional_frame_resource_data_use_->DidReceiveTransferSizeUpdate(
- received_data_length);
- } else if (page_timing_metrics_sender_) {
- page_timing_metrics_sender_->DidReceiveTransferSizeUpdate(
- request_id, received_data_length);
- UpdateResourceMetadata(request_id);
- }
- }
- void MetricsRenderFrameObserver::DidLoadResourceFromMemoryCache(
- const GURL& response_url,
- int request_id,
- int64_t encoded_body_length,
- const std::string& mime_type,
- bool from_archive) {
- // Resources from archives, such as subresources from a MHTML archive, do not
- // have valid request ids and should not be reported to PLM.
- if (from_archive)
- return;
- // A provisional frame resource cannot be serviced from the memory cache, so
- // we do not need to check |provisional_frame_resource_data_use_|.
- if (page_timing_metrics_sender_) {
- page_timing_metrics_sender_->DidLoadResourceFromMemoryCache(
- response_url, request_id, encoded_body_length, mime_type);
- MaybeSetCompletedBeforeFCP(request_id);
- UpdateResourceMetadata(request_id);
- }
- }
- void MetricsRenderFrameObserver::WillDetach() {
- if (page_timing_metrics_sender_) {
- page_timing_metrics_sender_->SendLatest();
- page_timing_metrics_sender_.reset();
- }
- }
- void MetricsRenderFrameObserver::DidStartNavigation(
- const GURL& url,
- absl::optional<blink::WebNavigationType> navigation_type) {
- // Send current metrics, as we might create a new RenderFrame later due to
- // this navigation (that might end up in a different process entirely, and
- // won't notify us until the current RenderFrameHost in the browser changed).
- // If that happens, it will be too late to send the metrics from WillDetach
- // or the destructor, because the browser ignores metrics update from
- // non-current RenderFrameHosts. See crbug.com/1150242 for more details.
- // TODO(crbug.com/1150242): Remove this when we have the full fix for the bug.
- if (page_timing_metrics_sender_)
- page_timing_metrics_sender_->SendLatest();
- }
- void MetricsRenderFrameObserver::DidSetPageLifecycleState() {
- // Send current metrics, as this RenderFrame might be replaced by a new
- // RenderFrame or its process might be killed, and this might be the last
- // point we can send the metrics to the browser. See crbug.com/1150242 for
- // more details.
- // TODO(crbug.com/1150242): Remove this when we have the full fix for the bug.
- if (page_timing_metrics_sender_)
- page_timing_metrics_sender_->SendLatest();
- }
- void MetricsRenderFrameObserver::ReadyToCommitNavigation(
- blink::WebDocumentLoader* document_loader) {
- // Create a new data use tracker for the new document load.
- provisional_frame_resource_data_use_ =
- std::make_unique<PageResourceDataUse>();
- provisional_frame_resource_id_ = 0;
- // Send current metrics before the next page load commits. Don't reset here
- // as it may be a same document load.
- if (page_timing_metrics_sender_)
- page_timing_metrics_sender_->SendLatest();
- }
- void MetricsRenderFrameObserver::DidFailProvisionalLoad() {
- // Clear the data use tracker for the provisional navigation that started.
- provisional_frame_resource_data_use_.reset();
- }
- 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;
- // 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>());
- OnMetricsSenderCreated();
- }
- 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();
- if (HasNoRenderFrame())
- return;
- // Update metadata once the load has been committed. There is no guarantee
- // that the provisional resource will have been reported as an ad by this
- // point. Therefore, need to update metadata for the resource after the load.
- // Consumers may receive the correct ad information late.
- UpdateResourceMetadata(provisional_frame_resource_data_use_->resource_id());
- provisional_frame_resource_id_ =
- provisional_frame_resource_data_use_->resource_id();
- Timing timing = GetTiming();
- page_timing_metrics_sender_ = std::make_unique<PageTimingMetricsSender>(
- CreatePageTimingSender(false /* limited_sending_mode*/), CreateTimer(),
- std::move(timing.relative_timing), timing.monotonic_timing,
- std::move(provisional_frame_resource_data_use_));
- OnMetricsSenderCreated();
- }
- void MetricsRenderFrameObserver::SetAdResourceTracker(
- subresource_filter::AdResourceTracker* ad_resource_tracker) {
- // Remove all sources and set a new source for the observer.
- scoped_ad_resource_observation_.Reset();
- scoped_ad_resource_observation_.Observe(ad_resource_tracker);
- }
- void MetricsRenderFrameObserver::OnAdResourceTrackerGoingAway() {
- DCHECK(scoped_ad_resource_observation_.IsObserving());
- scoped_ad_resource_observation_.Reset();
- }
- void MetricsRenderFrameObserver::OnAdResourceObserved(int request_id) {
- ad_request_ids_.insert(request_id);
- }
- void MetricsRenderFrameObserver::OnMainFrameIntersectionChanged(
- const gfx::Rect& main_frame_intersection_rect) {
- if (page_timing_metrics_sender_) {
- page_timing_metrics_sender_->OnMainFrameIntersectionChanged(
- main_frame_intersection_rect);
- return;
- }
- main_frame_intersection_rect_before_metrics_sender_created_ =
- main_frame_intersection_rect;
- }
- void MetricsRenderFrameObserver::OnMainFrameViewportRectangleChanged(
- const gfx::Rect& main_frame_viewport_rect) {
- if (page_timing_metrics_sender_) {
- page_timing_metrics_sender_->OnMainFrameViewportRectangleChanged(
- main_frame_viewport_rect);
- }
- }
- void MetricsRenderFrameObserver::OnMobileFriendlinessChanged(
- const blink::MobileFriendliness& mf) {
- if (page_timing_metrics_sender_)
- page_timing_metrics_sender_->DidObserveMobileFriendlinessChanged(mf);
- }
- bool MetricsRenderFrameObserver::SetUpSmoothnessReporting(
- base::ReadOnlySharedMemoryRegion& shared_memory) {
- if (page_timing_metrics_sender_) {
- page_timing_metrics_sender_->SetUpSmoothnessReporting(
- std::move(shared_memory));
- } else {
- ukm_smoothness_data_ = std::move(shared_memory);
- }
- return true;
- }
- void MetricsRenderFrameObserver::MaybeSetCompletedBeforeFCP(int request_id) {
- if (HasNoRenderFrame())
- return;
- const blink::WebPerformance& perf =
- render_frame()->GetWebFrame()->Performance();
- // Blink returns 0 if the performance metrics are unavailable. Check that
- // navigation start is set to determine if performance metrics are
- // available.
- if (perf.NavigationStart() == 0)
- return;
- // This should not be possible, but none the less occasionally fails in edge
- // case tests. Since we don't expect this to be valid, throw out this entry.
- // See crbug.com/1027535.
- if (base::Time::Now() < base::Time::FromDoubleT(perf.NavigationStart()))
- return;
- if (perf.FirstContentfulPaint() == 0)
- before_fcp_request_ids_.insert(request_id);
- }
- MetricsRenderFrameObserver::Timing::Timing(
- mojom::PageLoadTimingPtr relative_timing,
- const PageTimingMetadataRecorder::MonotonicTiming& monotonic_timing)
- : relative_timing(std::move(relative_timing)),
- monotonic_timing(monotonic_timing) {}
- MetricsRenderFrameObserver::Timing::~Timing() = default;
- MetricsRenderFrameObserver::Timing::Timing(Timing&&) = default;
- MetricsRenderFrameObserver::Timing& MetricsRenderFrameObserver::Timing::
- operator=(Timing&&) = default;
- void MetricsRenderFrameObserver::UpdateResourceMetadata(int request_id) {
- if (!page_timing_metrics_sender_)
- return;
- // If the request is an ad, pop it off the list of known ad requests.
- auto ad_resource_it = ad_request_ids_.find(request_id);
- bool reported_as_ad_resource =
- ad_request_ids_.find(request_id) != ad_request_ids_.end();
- if (reported_as_ad_resource)
- ad_request_ids_.erase(ad_resource_it);
- // If the request completed before fcp, pop it off the list of known
- // before-fcp requests.
- auto before_fcp_it = before_fcp_request_ids_.find(request_id);
- bool completed_before_fcp = before_fcp_it != before_fcp_request_ids_.end();
- if (completed_before_fcp) {
- before_fcp_request_ids_.erase(before_fcp_it);
- }
- bool is_main_frame_resource = render_frame()->IsMainFrame();
- if (provisional_frame_resource_data_use_ &&
- provisional_frame_resource_data_use_->resource_id() == request_id) {
- if (reported_as_ad_resource)
- provisional_frame_resource_data_use_->SetReportedAsAdResource(
- reported_as_ad_resource);
- provisional_frame_resource_data_use_->SetIsMainFrameResource(
- is_main_frame_resource);
- // Don't bother with before-fcp metrics for a provisional frame.
- } else {
- page_timing_metrics_sender_->UpdateResourceMetadata(
- request_id, reported_as_ad_resource, is_main_frame_resource,
- completed_before_fcp);
- }
- }
- void MetricsRenderFrameObserver::SendMetrics() {
- if (!page_timing_metrics_sender_)
- return;
- if (HasNoRenderFrame())
- return;
- Timing timing = GetTiming();
- page_timing_metrics_sender_->Update(std::move(timing.relative_timing),
- timing.monotonic_timing);
- }
- void MetricsRenderFrameObserver::OnMetricsSenderCreated() {
- if (ukm_smoothness_data_.IsValid()) {
- page_timing_metrics_sender_->SetUpSmoothnessReporting(
- std::move(ukm_smoothness_data_));
- }
- // Send the latest the frame intersection update, as otherwise we may miss
- // this information for a frame completely if there are no future updates.
- if (main_frame_intersection_rect_before_metrics_sender_created_) {
- page_timing_metrics_sender_->OnMainFrameIntersectionChanged(
- *main_frame_intersection_rect_before_metrics_sender_created_);
- main_frame_intersection_rect_before_metrics_sender_created_.reset();
- }
- }
- MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
- const {
- const blink::WebPerformance& perf =
- render_frame()->GetWebFrame()->Performance();
- mojom::PageLoadTimingPtr timing(CreatePageLoadTiming());
- PageTimingMetadataRecorder::MonotonicTiming monotonic_timing;
- double start = perf.NavigationStart();
- timing->navigation_start = base::Time::FromDoubleT(start);
- monotonic_timing.navigation_start = perf.NavigationStartAsMonotonicTime();
- if (perf.InputForNavigationStart() > 0.0) {
- timing->input_to_navigation_start =
- ClampDelta(start, perf.InputForNavigationStart());
- }
- if (perf.FirstInputDelay().has_value()) {
- timing->interactive_timing->first_input_delay = *perf.FirstInputDelay();
- monotonic_timing.first_input_delay = perf.FirstInputDelay();
- }
- if (perf.FirstInputTimestamp().has_value()) {
- timing->interactive_timing->first_input_timestamp =
- ClampDelta((*perf.FirstInputTimestamp()).InSecondsF(), start);
- }
- if (perf.FirstInputTimestampAsMonotonicTime()) {
- monotonic_timing.first_input_timestamp =
- perf.FirstInputTimestampAsMonotonicTime();
- }
- if (perf.LongestInputDelay().has_value()) {
- timing->interactive_timing->longest_input_delay = *perf.LongestInputDelay();
- }
- if (perf.LongestInputTimestamp().has_value()) {
- 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) {
- timing->document_timing->dom_content_loaded_event_start =
- ClampDelta(perf.DomContentLoadedEventStart(), start);
- }
- if (perf.LoadEventStart() > 0.0) {
- timing->document_timing->load_event_start =
- ClampDelta(perf.LoadEventStart(), start);
- }
- 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;
- absl::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);
- }
- for (double raf : restore_timing.request_animation_frames) {
- if (!raf)
- break;
- back_forward_cache_timing
- ->request_animation_frames_after_back_forward_cache_restore
- .push_back(ClampDelta(raf, navigation_start));
- }
- if (first_input_delay.has_value()) {
- back_forward_cache_timing
- ->first_input_delay_after_back_forward_cache_restore =
- first_input_delay;
- }
- 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 =
- ClampToStart(perf.FirstContentfulPaintAsMonotonicTime(),
- perf.NavigationStartAsMonotonicTime());
- }
- if (perf.FirstMeaningfulPaint() > 0.0) {
- timing->paint_timing->first_meaningful_paint =
- ClampDelta(perf.FirstMeaningfulPaint(), start);
- }
- if (perf.LargestImagePaintSize() > 0) {
- 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_contentful_paint->largest_image_paint =
- perf.LargestImagePaint() == 0.0
- ? base::TimeDelta()
- : ClampDelta(perf.LargestImagePaint(), start);
- timing->paint_timing->largest_contentful_paint->type =
- LargestContentfulPaintTypeToUKMFlags(perf.LargestContentfulPaintType());
- timing->paint_timing->largest_contentful_paint->image_bpp =
- perf.LargestContentfulPaintImageBPP();
- }
- if (perf.LargestTextPaintSize() > 0) {
- // 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_contentful_paint->largest_text_paint =
- ClampDelta(perf.LargestTextPaint(), start);
- 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);
- timing->paint_timing->experimental_largest_contentful_paint->type =
- LargestContentfulPaintTypeToUKMFlags(perf.LargestContentfulPaintType());
- }
- 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 =
- ClampDelta(perf.FirstInputOrScrollNotifiedTimestamp(), start);
- }
- if (perf.ParseStart() > 0.0)
- timing->parse_timing->parse_start = ClampDelta(perf.ParseStart(), start);
- if (perf.ParseStop() > 0.0)
- timing->parse_timing->parse_stop = ClampDelta(perf.ParseStop(), start);
- if (timing->parse_timing->parse_start) {
- // If we started parsing, record all parser durations such as the amount of
- // time blocked on script load, even if those values are zero.
- timing->parse_timing->parse_blocked_on_script_load_duration =
- base::Seconds(perf.ParseBlockedOnScriptLoadDuration());
- timing->parse_timing
- ->parse_blocked_on_script_load_from_document_write_duration =
- base::Seconds(perf.ParseBlockedOnScriptLoadFromDocumentWriteDuration());
- timing->parse_timing->parse_blocked_on_script_execution_duration =
- base::Seconds(perf.ParseBlockedOnScriptExecutionDuration());
- timing->parse_timing
- ->parse_blocked_on_script_execution_from_document_write_duration =
- base::Seconds(
- perf.ParseBlockedOnScriptExecutionFromDocumentWriteDuration());
- }
- if (perf.LastPortalActivatedPaint().has_value()) {
- timing->paint_timing->portal_activated_paint =
- *perf.LastPortalActivatedPaint();
- }
- if (perf.PrerenderActivationStart().has_value())
- timing->activation_start = perf.PrerenderActivationStart();
- if (perf.UserTimingMarkFullyLoaded().has_value())
- timing->user_timing_mark_fully_loaded = perf.UserTimingMarkFullyLoaded();
- if (perf.UserTimingMarkFullyVisible().has_value())
- timing->user_timing_mark_fully_visible = perf.UserTimingMarkFullyVisible();
- if (perf.UserTimingMarkInteractive().has_value())
- timing->user_timing_mark_interactive = perf.UserTimingMarkInteractive();
- return Timing(std::move(timing), monotonic_timing);
- }
- std::unique_ptr<base::OneShotTimer> MetricsRenderFrameObserver::CreateTimer() {
- return std::make_unique<base::OneShotTimer>();
- }
- std::unique_ptr<PageTimingSender>
- MetricsRenderFrameObserver::CreatePageTimingSender(bool limited_sending_mode) {
- return base::WrapUnique<PageTimingSender>(
- new MojoPageTimingSender(render_frame(), limited_sending_mode));
- }
- bool MetricsRenderFrameObserver::HasNoRenderFrame() const {
- bool no_frame = !render_frame() || !render_frame()->GetWebFrame();
- DCHECK(!no_frame);
- return no_frame;
- }
- void MetricsRenderFrameObserver::OnDestruct() {
- delete this;
- }
- } // namespace page_load_metrics
|