Expose presentationTime/paintTime in LoAF
This is an implementation of PaintTimingMixin in LoAF. It attempts to follow the MarkPaintTiming function in the spec, and queue the LoAF+paint timing entries together, when the presentation timestamp is ready. Bug: 378827535 Change-Id: I75e7a2dfd15666da8e2259a4919921d0d9747caa Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6038116 Reviewed-by: Yoav Weiss (@Shopify) <yoavweiss@chromium.org> Commit-Queue: Noam Rosenthal <nrosenthal@chromium.org> Cr-Commit-Position: refs/heads/main@{#1392240}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
efb3cffcd7
commit
1f65da8ca0
third_party/blink
renderer
core
frame
animation_frame_timing_monitor.ccanimation_frame_timing_monitor.hlocal_frame_view.ccweb_frame_widget_impl.ccweb_frame_widget_impl.h
paint
build.gni
timing
timing
animation_frame_timing_info.hperformance.ccperformance.hperformance_entry.ccperformance_entry.hperformance_long_animation_frame_timing.ccperformance_long_animation_frame_timing.hperformance_long_animation_frame_timing.idlperformance_paint_timing.ccperformance_paint_timing.hwindow_performance.ccwindow_performance.h
platform
widget
web_tests
external
wpt
largest-contentful-paint
long-animation-frame
tentative
paint-timing
webexposed
@@ -93,13 +93,14 @@ void AnimationFrameTimingMonitor::WillPerformStyleAndLayoutCalculation() {
|
||||
base::TimeTicks::Now());
|
||||
}
|
||||
|
||||
void AnimationFrameTimingMonitor::RecordRenderingUpdateEndTime(
|
||||
AnimationFrameTimingInfo*
|
||||
AnimationFrameTimingMonitor::RecordRenderingUpdateEndTime(
|
||||
LocalDOMWindow& local_root_window,
|
||||
base::TimeTicks rendering_update_end_time) {
|
||||
// This can happen if the AnimationFrameTimingMonitor instance is created
|
||||
// in the middle of a frame, or if some testing scenarios paint synchronously.
|
||||
if (!current_frame_timing_info_ || state_ != State::kRenderingFrame) {
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
current_frame_timing_info_->SetRenderEndTime(rendering_update_end_time);
|
||||
@@ -110,7 +111,9 @@ void AnimationFrameTimingMonitor::RecordRenderingUpdateEndTime(
|
||||
did_pause_ = false;
|
||||
|
||||
current_frame_timing_info_->SetScripts(current_scripts_);
|
||||
AnimationFrameTimingInfo* long_animation_frame = nullptr;
|
||||
if (current_frame_timing_info_->Duration() >= kLongAnimationFrameDuration) {
|
||||
long_animation_frame = current_frame_timing_info_;
|
||||
if (!first_ui_event_timestamp_.is_null()) {
|
||||
current_frame_timing_info_->SetFirstUIEventTime(
|
||||
first_ui_event_timestamp_);
|
||||
@@ -134,8 +137,6 @@ void AnimationFrameTimingMonitor::RecordRenderingUpdateEndTime(
|
||||
}
|
||||
|
||||
current_frame_timing_info_->SetTotalBlockingDuration(blocking_duration);
|
||||
|
||||
client_.ReportLongAnimationFrameTiming(current_frame_timing_info_);
|
||||
}
|
||||
RecordLongAnimationFrameUKMAndTrace(*current_frame_timing_info_,
|
||||
local_root_window);
|
||||
@@ -146,6 +147,7 @@ void AnimationFrameTimingMonitor::RecordRenderingUpdateEndTime(
|
||||
longest_task_duration_ = total_blocking_time_excluding_longest_task_ =
|
||||
base::TimeDelta();
|
||||
state_ = State::kIdle;
|
||||
return long_animation_frame;
|
||||
}
|
||||
|
||||
void AnimationFrameTimingMonitor::WillProcessTask(base::TimeTicks start_time) {
|
||||
@@ -271,7 +273,7 @@ void AnimationFrameTimingMonitor::OnTaskCompleted(
|
||||
}
|
||||
|
||||
DOMWindowPerformance::performance(*frame->DomWindow())
|
||||
->ReportLongAnimationFrameTiming(timing_info);
|
||||
->QueueLongAnimationFrameTiming(timing_info);
|
||||
RecordLongAnimationFrameUKMAndTrace(*timing_info, *frame->DomWindow());
|
||||
}
|
||||
|
||||
@@ -316,10 +318,10 @@ void AnimationFrameTimingMonitor::RequestPresentationTimeForTracing(
|
||||
TRACE_EVENT_CATEGORY_GROUP_ENABLED("devtools.timeline", &tracing_enabled);
|
||||
if (tracing_enabled) {
|
||||
frame.GetChromeClient().NotifyPresentationTime(
|
||||
frame,
|
||||
BindOnce(&AnimationFrameTimingMonitor::ReportPresentationTimeToTrace,
|
||||
WrapWeakPersistent(this),
|
||||
current_frame_timing_info_->GetTraceId()));
|
||||
frame, WTF::BindOnce(
|
||||
&AnimationFrameTimingMonitor::ReportPresentationTimeToTrace,
|
||||
WrapWeakPersistent(this),
|
||||
current_frame_timing_info_->GetTraceId()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,6 @@ class CORE_EXPORT AnimationFrameTimingMonitor final
|
||||
public:
|
||||
class Client {
|
||||
public:
|
||||
virtual void ReportLongAnimationFrameTiming(AnimationFrameTimingInfo*) = 0;
|
||||
virtual void ReportLongTaskTiming(base::TimeTicks start,
|
||||
base::TimeTicks end,
|
||||
ExecutionContext* context) = 0;
|
||||
@@ -55,8 +54,9 @@ class CORE_EXPORT AnimationFrameTimingMonitor final
|
||||
|
||||
void BeginMainFrame(LocalDOMWindow& local_root_window);
|
||||
void WillPerformStyleAndLayoutCalculation();
|
||||
void RecordRenderingUpdateEndTime(LocalDOMWindow& local_root_window,
|
||||
base::TimeTicks);
|
||||
AnimationFrameTimingInfo* RecordRenderingUpdateEndTime(
|
||||
LocalDOMWindow& local_root_window,
|
||||
base::TimeTicks);
|
||||
void OnTaskCompleted(base::TimeTicks start_time,
|
||||
base::TimeTicks end_time,
|
||||
LocalFrame* frame);
|
||||
|
@@ -2367,21 +2367,9 @@ void LocalFrameView::UpdateLifecyclePhasesInternal(
|
||||
break;
|
||||
}
|
||||
|
||||
// Updating the rendering time here, after view transition operations, as per
|
||||
// spec. Note that "pre-paint" has already occurred, because it's needed for
|
||||
// view transitions and resize observers.
|
||||
base::TimeTicks rendering_update_end_time = base::TimeTicks::Now();
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#update-the-rendering
|
||||
// 20. For each doc of docs, record rendering time for doc..
|
||||
if (FrameWidget* widget = frame_->GetWidgetForLocalRoot()) {
|
||||
widget->RecordRenderingUpdateEndTime(rendering_update_end_time);
|
||||
}
|
||||
|
||||
// 21. For each doc of docs, mark paint timing for doc.
|
||||
ForAllNonThrottledLocalFrameViews([&](LocalFrameView& frame_view) {
|
||||
PaintTiming::From(*frame_view.frame_->GetDocument())
|
||||
.SetRenderingUpdateEndTime(rendering_update_end_time);
|
||||
PaintTiming::From(*frame_view.frame_->GetDocument()).MarkPaintTiming();
|
||||
});
|
||||
|
||||
// This must be after all other updates for position-visibility.
|
||||
|
@@ -52,6 +52,7 @@
|
||||
#include "cc/trees/layer_tree_host.h"
|
||||
#include "cc/trees/swap_promise.h"
|
||||
#include "third_party/blink/public/common/features.h"
|
||||
#include "third_party/blink/public/common/metrics/document_update_reason.h"
|
||||
#include "third_party/blink/public/common/page/page_zoom.h"
|
||||
#include "third_party/blink/public/mojom/frame/intrinsic_sizing_info.mojom-blink.h"
|
||||
#include "third_party/blink/public/mojom/input/input_handler.mojom-blink.h"
|
||||
@@ -1665,20 +1666,6 @@ WebFrameWidgetImpl::AllocateNewLayerTreeFrameSink() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WebFrameWidgetImpl::ReportLongAnimationFrameTiming(
|
||||
AnimationFrameTimingInfo* timing_info) {
|
||||
WebSecurityOrigin root_origin = local_root_->GetSecurityOrigin();
|
||||
ForEachLocalFrameControlledByWidget(
|
||||
local_root_->GetFrame(), [&](WebLocalFrameImpl* local_frame) {
|
||||
if (local_frame == local_root_ ||
|
||||
!local_frame->GetSecurityOrigin().IsSameOriginWith(root_origin)) {
|
||||
DOMWindowPerformance::performance(
|
||||
*local_frame->GetFrame()->DomWindow())
|
||||
->ReportLongAnimationFrameTiming(timing_info);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void WebFrameWidgetImpl::ReportLongTaskTiming(base::TimeTicks start_time,
|
||||
base::TimeTicks end_time,
|
||||
ExecutionContext* task_context) {
|
||||
@@ -1709,16 +1696,16 @@ void WebFrameWidgetImpl::OnTaskCompletedForFrame(
|
||||
}
|
||||
}
|
||||
|
||||
void WebFrameWidgetImpl::RecordRenderingUpdateEndTime(
|
||||
AnimationFrameTimingInfo* WebFrameWidgetImpl::RecordRenderingUpdateEndTime(
|
||||
base::TimeTicks rendering_update_time) {
|
||||
if (!animation_frame_timing_monitor_) {
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LocalFrame* local_root_frame = LocalRootImpl()->GetFrame();
|
||||
CHECK(local_root_frame);
|
||||
CHECK(local_root_frame->DomWindow());
|
||||
animation_frame_timing_monitor_->RecordRenderingUpdateEndTime(
|
||||
return animation_frame_timing_monitor_->RecordRenderingUpdateEndTime(
|
||||
*local_root_frame->DomWindow(), rendering_update_time);
|
||||
}
|
||||
|
||||
@@ -3670,6 +3657,7 @@ void WebFrameWidgetImpl::NotifySwapAndPresentationTimeForTesting(
|
||||
void WebFrameWidgetImpl::NotifyPresentationTime(
|
||||
base::OnceCallback<void(const viz::FrameTimingDetails&)>
|
||||
presentation_callback) {
|
||||
CHECK(IsMainThread());
|
||||
NotifySwapAndPresentationTime(
|
||||
{.presentation_time_callback = std::move(presentation_callback)});
|
||||
}
|
||||
|
@@ -333,13 +333,13 @@ class CORE_EXPORT WebFrameWidgetImpl
|
||||
bool GetMayThrottleIfUndrawnFramesForTesting();
|
||||
|
||||
// AnimationFrameTimingMonitor::Client overrides
|
||||
void ReportLongAnimationFrameTiming(AnimationFrameTimingInfo* info) override;
|
||||
bool ShouldReportLongAnimationFrameTiming() const override;
|
||||
void ReportLongTaskTiming(base::TimeTicks start_time,
|
||||
base::TimeTicks end,
|
||||
ExecutionContext* task_context) override;
|
||||
bool RequestedMainFramePending() override;
|
||||
void RecordRenderingUpdateEndTime(base::TimeTicks) override;
|
||||
AnimationFrameTimingInfo* RecordRenderingUpdateEndTime(
|
||||
base::TimeTicks) override;
|
||||
ukm::UkmRecorder* MainFrameUkmRecorder() override;
|
||||
ukm::SourceId MainFrameUkmSourceId() override;
|
||||
|
||||
|
@@ -205,7 +205,6 @@ blink_core_sources_paint = [
|
||||
"timing/paint_timing_detector.cc",
|
||||
"timing/paint_timing_detector.h",
|
||||
"timing/paint_timing.h",
|
||||
"timing/paint_timing_info.h",
|
||||
"timing/paint_timing_visualizer.cc",
|
||||
"timing/paint_timing_visualizer.h",
|
||||
"timing/text_element_timing.cc",
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/functional/callback_forward.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/time/default_tick_clock.h"
|
||||
@@ -23,16 +24,18 @@
|
||||
#include "third_party/blink/renderer/core/page/chrome_client.h"
|
||||
#include "third_party/blink/renderer/core/page/page.h"
|
||||
#include "third_party/blink/renderer/core/probe/core_probes.h"
|
||||
#include "third_party/blink/renderer/core/timing/animation_frame_timing_info.h"
|
||||
#include "third_party/blink/renderer/core/timing/dom_window_performance.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_entry.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_timing_for_reporting.h"
|
||||
#include "third_party/blink/renderer/core/timing/window_performance.h"
|
||||
#include "third_party/blink/renderer/platform/graphics/paint/ignore_paint_timing_scope.h"
|
||||
#include "third_party/blink/renderer/platform/heap/cross_thread_handle.h"
|
||||
#include "third_party/blink/renderer/platform/heap/persistent.h"
|
||||
#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.h"
|
||||
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
|
||||
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
|
||||
#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
|
||||
#include "third_party/blink/renderer/platform/widget/frame_widget.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/functional.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/wtf.h"
|
||||
|
||||
@@ -48,6 +51,12 @@ WindowPerformance* GetPerformanceInstance(LocalFrame* frame) {
|
||||
return performance;
|
||||
}
|
||||
|
||||
struct PendingPaintTimingRecord {
|
||||
HashSet<PaintEvent> paint_events;
|
||||
bool is_soft_navigation = false;
|
||||
base::TimeTicks rendering_update_end_time;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class RecodingTimeAfterBackForwardCacheRestoreFrameCallback
|
||||
@@ -120,6 +129,7 @@ void PaintTiming::MarkFirstPaint() {
|
||||
}
|
||||
DCHECK_EQ(IgnorePaintTimingScope::IgnoreDepth(), 0);
|
||||
SetFirstPaint(clock_->NowTicks());
|
||||
pending_paint_events_.insert(PaintEvent::kFirstPaint);
|
||||
}
|
||||
|
||||
void PaintTiming::MarkFirstContentfulPaint() {
|
||||
@@ -144,7 +154,7 @@ void PaintTiming::MarkFirstImagePaint() {
|
||||
DCHECK_EQ(IgnorePaintTimingScope::IgnoreDepth(), 0);
|
||||
relevant_paint_details.first_image_paint_ = clock_->NowTicks();
|
||||
SetFirstContentfulPaint(relevant_paint_details.first_image_paint_);
|
||||
RegisterNotifyPresentationTime(PaintEvent::kFirstImagePaint);
|
||||
Mark(PaintEvent::kFirstImagePaint);
|
||||
}
|
||||
|
||||
void PaintTiming::MarkFirstEligibleToPaint() {
|
||||
@@ -203,6 +213,7 @@ void PaintTiming::NotifyPaint(bool is_first_paint,
|
||||
bool image_painted) {
|
||||
if (IgnorePaintTimingScope::IgnoreDepth() > 0)
|
||||
return;
|
||||
|
||||
if (is_first_paint)
|
||||
MarkFirstPaint();
|
||||
if (text_painted)
|
||||
@@ -213,6 +224,137 @@ void PaintTiming::NotifyPaint(bool is_first_paint,
|
||||
|
||||
if (is_first_paint)
|
||||
GetFrame()->OnFirstPaint(text_painted, image_painted);
|
||||
MarkPaintTimingInternal();
|
||||
}
|
||||
|
||||
// https://w3c.github.io/paint-timing/#mark-paint-timing
|
||||
void PaintTiming::MarkPaintTiming() {
|
||||
// 2. Let paintTimingInfo be a new paint timing info, whose rendering update
|
||||
// end time is the current high resolution time given document’s relevant
|
||||
// global object.
|
||||
last_rendering_update_end_time_ = base::TimeTicks::Now();
|
||||
// This continues in MarkPaintTimingInternal(), once we've received the paint
|
||||
// callbacks.
|
||||
}
|
||||
|
||||
void PaintTiming::MarkPaintTimingInternal() {
|
||||
FrameWidget* widget = GetFrame()->GetWidgetForLocalRoot();
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3-6: TODO(crbug.com/381270287) export PaintTimingMixin for
|
||||
// LCP/element-timing.
|
||||
|
||||
// 7. Let reportedPaints be the document’s set of previously reported paints.
|
||||
PendingPaintTimingRecord paint_timing_record{
|
||||
.paint_events = pending_paint_events_,
|
||||
.is_soft_navigation = first_paints_reset_,
|
||||
.rendering_update_end_time = last_rendering_update_end_time_};
|
||||
pending_paint_events_.clear();
|
||||
|
||||
// 8. Let frameTimingInfo be document’s current frame timing info.
|
||||
// 9. Set document’s current frame timing info to null.
|
||||
// (RecordRenderingUpdateEndTime resets the |current frame timing info| inside
|
||||
// AnimationFrameTimingMonitor, so it reads a bit different from the spec).
|
||||
AnimationFrameTimingInfo* frame_timing_info =
|
||||
widget->RecordRenderingUpdateEndTime(last_rendering_update_end_time_);
|
||||
|
||||
if (paint_timing_record.paint_events.empty() && !frame_timing_info) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 10. Let flushPaintTimings be the following steps:
|
||||
base::OnceCallback<void(WindowPerformance*, const DOMPaintTimingInfo&)>
|
||||
flush_paint_timings = WTF::BindOnce(
|
||||
[](const PendingPaintTimingRecord& record,
|
||||
AnimationFrameTimingInfo* frame_timing_info,
|
||||
WindowPerformance* performance,
|
||||
const DOMPaintTimingInfo& paint_timing_info) {
|
||||
// 10.1. If document should report first paint,
|
||||
// then: Report paint timing given document,
|
||||
// "first-paint", and paintTimingInfo.
|
||||
if (record.paint_events.Contains(PaintEvent::kFirstPaint)) {
|
||||
performance->AddFirstPaintTiming(paint_timing_info,
|
||||
record.is_soft_navigation);
|
||||
}
|
||||
|
||||
// 10.2. If document should report first contentful paint,
|
||||
// then: Report paint timing given document,
|
||||
// "first-contentful-paint", and paintTimingInfo.
|
||||
if (record.paint_events.Contains(
|
||||
PaintEvent::kFirstContentfulPaint)) {
|
||||
performance->AddFirstContentfulPaintTiming(
|
||||
paint_timing_info, record.is_soft_navigation);
|
||||
}
|
||||
|
||||
// TODO(crbug.com/381270287): 10.3, 10.4 (LCP and element timing).
|
||||
|
||||
// 10.5 If frameTimingInfo is not null, then queue a long
|
||||
// animation frame entry given document, frameTimingInfo, and
|
||||
// paintTimingInfo.
|
||||
if (frame_timing_info) {
|
||||
performance->QueueLongAnimationFrameTiming(frame_timing_info,
|
||||
paint_timing_info);
|
||||
}
|
||||
},
|
||||
paint_timing_record, WrapPersistent(frame_timing_info));
|
||||
|
||||
// 11. If the user-agent does not support implementation-defined presentation
|
||||
// times, call flushPaintTimings and return.
|
||||
|
||||
// 12. Run the following steps In parallel:
|
||||
// 12.1 Wait until an implementation-defined time when the current frame has
|
||||
// been presented to the user.
|
||||
RegisterNotifyPresentationTime(WTF::BindOnce(
|
||||
[](PaintTiming* self,
|
||||
base::OnceCallback<void(WindowPerformance*, const DOMPaintTimingInfo&)>
|
||||
flush_paint_timings,
|
||||
const PendingPaintTimingRecord& record,
|
||||
const viz::FrameTimingDetails& frame_timing_details) {
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Internal presentation times are not over-coarsened as they are not
|
||||
// web-exposed.
|
||||
for (PaintEvent event : record.paint_events) {
|
||||
self->ReportPresentationTime(event, record.rendering_update_end_time,
|
||||
frame_timing_details);
|
||||
}
|
||||
|
||||
WindowPerformance* performance =
|
||||
GetPerformanceInstance(self->GetFrame());
|
||||
if (!performance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 12.2 Set paintTimingInfo’s implementation-defined presentation time
|
||||
// to the current high resolution time given document’s relevant global
|
||||
// object.
|
||||
// (Note: the "current time" is acquired in parallel inside
|
||||
// RegisterNotifyPresentationTime, not here).
|
||||
// 12.3 If document’s cross-origin isolated capability is false, then:
|
||||
//
|
||||
// 12.3.1 Coarsen paintTimingInfo’s implementation-defined presentation
|
||||
// time to the next multiple of 4 milliseconds, or coarser. (The
|
||||
// coarsening happens in Performance::RenderTimeToDOMHighResTimeStamp)
|
||||
DOMPaintTimingInfo paint_timing_info{
|
||||
.paint_time = performance->MonotonicTimeToDOMHighResTimeStamp(
|
||||
record.rendering_update_end_time),
|
||||
.presentation_time = performance->RenderTimeToDOMHighResTimeStamp(
|
||||
frame_timing_details.presentation_feedback.timestamp)};
|
||||
|
||||
// 12.3.2 Wait until the current high resolution time is
|
||||
// paintTimingInfo’s implementation-defined presentation time.
|
||||
// GetPerformanceInstance(self->GetFrame())->QueueEntryWithPaintTiming(
|
||||
// 12.4 Queue a global task on the performance timeline task source
|
||||
// given document’s relevant global object to run flushPaintTimings.
|
||||
performance->QueueEntryWithPaintTiming(std::move(flush_paint_timings),
|
||||
paint_timing_info);
|
||||
},
|
||||
WrapWeakPersistent(this), std::move(flush_paint_timings),
|
||||
paint_timing_record));
|
||||
}
|
||||
|
||||
void PaintTiming::SetTickClockForTesting(const base::TickClock* clock) {
|
||||
@@ -247,7 +389,6 @@ void PaintTiming::SetFirstPaint(base::TimeTicks stamp) {
|
||||
DCHECK_EQ(IgnorePaintTimingScope::IgnoreDepth(), 0);
|
||||
|
||||
relevant_paint_details.first_paint_ = stamp;
|
||||
RegisterNotifyPresentationTime(PaintEvent::kFirstPaint);
|
||||
|
||||
if (!first_paints_reset_) {
|
||||
LocalFrame* frame = GetFrame();
|
||||
@@ -255,6 +396,8 @@ void PaintTiming::SetFirstPaint(base::TimeTicks stamp) {
|
||||
frame->GetDocument()->MarkFirstPaint();
|
||||
}
|
||||
}
|
||||
|
||||
pending_paint_events_.insert(PaintEvent::kFirstPaint);
|
||||
}
|
||||
|
||||
void PaintTiming::SetFirstContentfulPaint(base::TimeTicks stamp) {
|
||||
@@ -279,21 +422,15 @@ void PaintTiming::SetFirstContentfulPaint(base::TimeTicks stamp) {
|
||||
}
|
||||
}
|
||||
SetFirstPaint(stamp);
|
||||
RegisterNotifyPresentationTime(PaintEvent::kFirstContentfulPaint);
|
||||
Mark(PaintEvent::kFirstContentfulPaint);
|
||||
|
||||
if (!first_paints_reset_ || soft_navigation_detected_) {
|
||||
NotifyPaintTimingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void PaintTiming::RegisterNotifyPresentationTime(PaintEvent event) {
|
||||
// https://w3c.github.io/paint-timing/#mark-paint-timing
|
||||
// 10.1 Wait until an implementation-defined time when the current frame has
|
||||
// been presented to the user.
|
||||
|
||||
RegisterNotifyPresentationTime(WTF::BindOnce(
|
||||
&PaintTiming::ReportPresentationTime, WrapWeakPersistent(this), event,
|
||||
last_rendering_update_end_time_));
|
||||
void PaintTiming::Mark(PaintEvent event) {
|
||||
pending_paint_events_.insert(event);
|
||||
}
|
||||
|
||||
void PaintTiming::
|
||||
@@ -401,12 +538,6 @@ void PaintTiming::SetFirstPaintPresentation(
|
||||
GetSupplementable(), "firstPaint",
|
||||
relevant_paint_details.first_paint_presentation_.since_origin()
|
||||
.InSecondsF());
|
||||
WindowPerformance* performance = GetPerformanceInstance(GetFrame());
|
||||
if (performance) {
|
||||
performance->AddFirstPaintTiming(
|
||||
paint_timing_info,
|
||||
/*is_triggered_by_soft_navigation=*/first_paints_reset_);
|
||||
}
|
||||
NotifyPaintTimingChanged();
|
||||
if (first_paints_reset_) {
|
||||
soft_navigation_fp_reported_ = true;
|
||||
@@ -447,11 +578,6 @@ void PaintTiming::SetFirstContentfulPaintPresentation(
|
||||
relevant_paint_details.first_contentful_paint_presentation_.since_origin()
|
||||
.InSecondsF());
|
||||
WindowPerformance* performance = GetPerformanceInstance(GetFrame());
|
||||
if (performance) {
|
||||
performance->AddFirstContentfulPaintTiming(
|
||||
paint_timing_info,
|
||||
/*is_triggered_by_soft_navigation=*/first_paints_reset_);
|
||||
}
|
||||
// For soft navigations, we just want to report a performance entry, but not
|
||||
// trigger any of the other FCP observers.
|
||||
if (is_soft_navigation_fcp) {
|
||||
|
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/functional/callback_forward.h"
|
||||
#include "base/gtest_prod_util.h"
|
||||
#include "base/time/time.h"
|
||||
#include "components/viz/common/frame_timing_details.h"
|
||||
@@ -16,7 +15,7 @@
|
||||
#include "third_party/blink/renderer/core/dom/document.h"
|
||||
#include "third_party/blink/renderer/core/paint/paint_event.h"
|
||||
#include "third_party/blink/renderer/core/paint/timing/first_meaningful_paint_detector.h"
|
||||
#include "third_party/blink/renderer/core/paint/timing/paint_timing_info.h"
|
||||
#include "third_party/blink/renderer/core/timing/animation_frame_timing_info.h"
|
||||
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
|
||||
#include "third_party/blink/renderer/platform/supplementable.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/functional.h"
|
||||
@@ -27,6 +26,7 @@ class TickClock;
|
||||
|
||||
namespace blink {
|
||||
|
||||
struct DOMPaintTimingInfo;
|
||||
class LocalFrame;
|
||||
|
||||
// PaintTiming is responsible for tracking paint-related timings for a given
|
||||
@@ -44,6 +44,14 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>,
|
||||
public:
|
||||
static const char kSupplementName[];
|
||||
|
||||
struct PaintTimingInfo {
|
||||
// https://w3c.github.io/paint-timing/#paint-timing-info-rendering-update-end-time
|
||||
base::TimeTicks rendering_update_end_time;
|
||||
|
||||
// https://w3c.github.io/paint-timing/#paint-timing-info-implementation-defined-presentation-time
|
||||
base::TimeTicks presentation_time;
|
||||
};
|
||||
|
||||
explicit PaintTiming(Document&);
|
||||
PaintTiming(const PaintTiming&) = delete;
|
||||
PaintTiming& operator=(const PaintTiming&) = delete;
|
||||
@@ -80,6 +88,7 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>,
|
||||
base::TimeTicks presentation_time,
|
||||
FirstMeaningfulPaintDetector::HadUserInput had_input);
|
||||
void NotifyPaint(bool is_first_paint, bool text_painted, bool image_painted);
|
||||
void NotifyPaintFinished() { MarkPaintTimingInternal(); }
|
||||
|
||||
// The getters below return monotonically-increasing seconds, or zero if the
|
||||
// given paint event has not yet occurred. See the comments for
|
||||
@@ -174,9 +183,7 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>,
|
||||
|
||||
void SoftNavigationDetected();
|
||||
|
||||
void SetRenderingUpdateEndTime(base::TimeTicks rendering_update_end_time) {
|
||||
last_rendering_update_end_time_ = rendering_update_end_time;
|
||||
}
|
||||
void MarkPaintTiming();
|
||||
|
||||
void Trace(Visitor*) const override;
|
||||
|
||||
@@ -186,6 +193,8 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>,
|
||||
LocalFrame* GetFrame() const;
|
||||
void NotifyPaintTimingChanged();
|
||||
|
||||
void MarkPaintTimingInternal();
|
||||
|
||||
// Set*() set the timing for the given paint event to the given timestamp if
|
||||
// the value is currently zero, and queue a presentation promise to record the
|
||||
// |first_*_presentation_| timestamp. These methods can be invoked from other
|
||||
@@ -216,7 +225,7 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>,
|
||||
void SetRequestAnimationFrameAfterBackForwardCacheRestore(wtf_size_t index,
|
||||
size_t count);
|
||||
|
||||
void RegisterNotifyPresentationTime(PaintEvent);
|
||||
void Mark(PaintEvent);
|
||||
void RegisterNotifyFirstPaintAfterBackForwardCacheRestorePresentationTime(
|
||||
wtf_size_t index);
|
||||
|
||||
@@ -245,6 +254,8 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>,
|
||||
: paint_details_;
|
||||
}
|
||||
|
||||
DOMPaintTimingInfo ToDOMPaintTimingInfo(const PaintTimingInfo&) const;
|
||||
|
||||
PaintDetails paint_details_;
|
||||
PaintDetails soft_navigation_pending_paint_details_;
|
||||
std::optional<PaintTimingInfo>
|
||||
@@ -269,7 +280,6 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>,
|
||||
base::TimeTicks lcp_mouse_over_dispatch_time_;
|
||||
|
||||
Member<FirstMeaningfulPaintDetector> fmp_detector_;
|
||||
|
||||
// The callback ID for requestAnimationFrame to record its time after the page
|
||||
// is restored from the back-forward cache.
|
||||
int raf_after_bfcache_restore_measurement_callback_id_ = 0;
|
||||
@@ -278,6 +288,7 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>,
|
||||
|
||||
FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest,
|
||||
TwoLayoutsSignificantFirst);
|
||||
HashSet<PaintEvent> pending_paint_events_;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
@@ -9,8 +9,6 @@
|
||||
#include "third_party/blink/renderer/core/page/chrome_client.h"
|
||||
#include "third_party/blink/renderer/core/page/page.h"
|
||||
#include "third_party/blink/renderer/core/paint/timing/paint_timing_detector.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_std.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "third_party/blink/renderer/core/paint/paint_layer.h"
|
||||
#include "third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.h"
|
||||
#include "third_party/blink/renderer/core/paint/timing/largest_contentful_paint_calculator.h"
|
||||
#include "third_party/blink/renderer/core/paint/timing/paint_timing.h"
|
||||
#include "third_party/blink/renderer/core/paint/timing/text_paint_timing_detector.h"
|
||||
#include "third_party/blink/renderer/core/style/style_fetched_image.h"
|
||||
#include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
|
||||
@@ -167,6 +168,9 @@ void PaintTimingDetector::NotifyPaintFinished() {
|
||||
if (window) {
|
||||
DOMWindowPerformance::performance(*window)->OnPaintFinished();
|
||||
}
|
||||
|
||||
PaintTiming::From(*frame_view_->GetFrame().GetDocument())
|
||||
.NotifyPaintFinished();
|
||||
}
|
||||
|
||||
// static
|
||||
|
@@ -1,23 +0,0 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TIMING_PAINT_TIMING_INFO_H_
|
||||
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TIMING_PAINT_TIMING_INFO_H_
|
||||
|
||||
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
// https://w3c.github.io/paint-timing/#paint-timing-info
|
||||
struct PaintTimingInfo {
|
||||
// https://w3c.github.io/paint-timing/#paint-timing-info-rendering-update-end-time
|
||||
base::TimeTicks rendering_update_end_time;
|
||||
|
||||
// https://w3c.github.io/paint-timing/#paint-timing-info-implementation-defined-presentation-time
|
||||
base::TimeTicks presentation_time;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
||||
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TIMING_PAINT_TIMING_INFO_H_
|
@@ -106,6 +106,7 @@ class AnimationFrameTimingInfo
|
||||
}
|
||||
|
||||
void SetRenderEndTime(base::TimeTicks time) { render_end_time = time; }
|
||||
void SetPresentationTime(base::TimeTicks time) { presentation_time = time; }
|
||||
void SetFirstUIEventTime(base::TimeTicks time) { first_ui_event_time = time; }
|
||||
|
||||
base::TimeTicks FrameStartTime() const { return frame_start_time; }
|
||||
@@ -114,6 +115,7 @@ class AnimationFrameTimingInfo
|
||||
return style_and_layout_start_time;
|
||||
}
|
||||
base::TimeTicks RenderEndTime() const { return render_end_time; }
|
||||
base::TimeTicks PresentationTime() const { return presentation_time; }
|
||||
base::TimeTicks FirstUIEventTime() const { return first_ui_event_time; }
|
||||
base::TimeDelta Duration() const {
|
||||
return RenderEndTime() - FrameStartTime();
|
||||
@@ -157,6 +159,9 @@ class AnimationFrameTimingInfo
|
||||
// a main frame update
|
||||
base::TimeTicks render_end_time;
|
||||
|
||||
// Measured when the frame is presented to the user.
|
||||
base::TimeTicks presentation_time;
|
||||
|
||||
// The event timestamp of the first UI event that coincided with the frame.
|
||||
base::TimeTicks first_ui_event_time;
|
||||
|
||||
|
@@ -32,7 +32,7 @@
|
||||
#include "base/check_op.h"
|
||||
#include "base/time/time.h"
|
||||
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
|
||||
#include "third_party/blink/renderer/core/paint/timing/paint_timing_info.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_paint_timing.h"
|
||||
#ifdef UNSAFE_BUFFERS_BUILD
|
||||
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
|
||||
#pragma allow_unsafe_buffers
|
||||
@@ -843,113 +843,6 @@ void Performance::AddSoftNavigationToPerformanceTimeline(
|
||||
}
|
||||
}
|
||||
|
||||
void Performance::AddRenderCoarsenedEntry(
|
||||
base::OnceCallback<void(Performance&)> callback,
|
||||
DOMHighResTimeStamp earliest_timestamp_for_timeline) {
|
||||
if (!RuntimeEnabledFeatures::ExposeCoarsenedRenderTimeEnabled() ||
|
||||
time_origin_.is_null() || cross_origin_isolated_capability_) {
|
||||
std::move(callback).Run(*this);
|
||||
return;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/paint-timing/#mark-paint-timing
|
||||
// 10.3.2 Wait until the current high resolution time is paintTimingInfo’s
|
||||
// implementation-defined presentation time.
|
||||
base::TimeTicks target_time =
|
||||
time_origin_ + base::Milliseconds(earliest_timestamp_for_timeline);
|
||||
if (pending_entry_operations_with_render_coarsening_.empty()) {
|
||||
SchedulePendingRenderCoarsenedEntries(target_time);
|
||||
}
|
||||
|
||||
pending_entry_operations_with_render_coarsening_.push_back(
|
||||
std::make_pair(std::move(callback), target_time));
|
||||
}
|
||||
|
||||
void Performance::SchedulePendingRenderCoarsenedEntries(
|
||||
base::TimeTicks target_time) {
|
||||
task_runner_->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
WTF::BindOnce(
|
||||
[](WeakPersistent<Performance> self) {
|
||||
if (self) {
|
||||
self->FlushPendingRenderCoarsenedEntries();
|
||||
}
|
||||
},
|
||||
WrapWeakPersistent(this)),
|
||||
target_time - base::TimeTicks::Now());
|
||||
}
|
||||
|
||||
void Performance::FlushPendingRenderCoarsenedEntries() {
|
||||
base::TimeTicks now = base::TimeTicks::Now();
|
||||
|
||||
Vector<std::pair<base::OnceCallback<void(Performance&)>, base::TimeTicks>>
|
||||
pending_entries;
|
||||
std::swap(pending_entry_operations_with_render_coarsening_, pending_entries);
|
||||
base::TimeTicks next_tick;
|
||||
for (auto& [callback, target_time] : pending_entries) {
|
||||
// We could have had a few entries batched and this one is coarsened to the
|
||||
// future. Fire it in the next batch.
|
||||
if (target_time > now) {
|
||||
pending_entry_operations_with_render_coarsening_.push_back(
|
||||
std::make_pair(std::move(callback), target_time));
|
||||
next_tick =
|
||||
next_tick.is_null() ? target_time : std::min(next_tick, target_time);
|
||||
} else {
|
||||
std::move(callback).Run(*this);
|
||||
}
|
||||
}
|
||||
|
||||
if (!next_tick.is_null()) {
|
||||
SchedulePendingRenderCoarsenedEntries(next_tick);
|
||||
}
|
||||
}
|
||||
|
||||
void Performance::AddFirstPaintTiming(const PaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation) {
|
||||
AddPaintTiming(PerformancePaintTiming::PaintType::kFirstPaint,
|
||||
paint_timing_info, is_triggered_by_soft_navigation);
|
||||
}
|
||||
|
||||
void Performance::AddFirstContentfulPaintTiming(
|
||||
const PaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation) {
|
||||
AddPaintTiming(PerformancePaintTiming::PaintType::kFirstContentfulPaint,
|
||||
paint_timing_info, is_triggered_by_soft_navigation);
|
||||
}
|
||||
|
||||
void Performance::AddPaintTiming(PerformancePaintTiming::PaintType type,
|
||||
const PaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation) {
|
||||
PerformanceEntry* entry = MakeGarbageCollected<PerformancePaintTiming>(
|
||||
type,
|
||||
// https://w3c.github.io/paint-timing/#mark-paint-timing
|
||||
// 10.3.1 Coarsen paintTimingInfo’s implementation-defined presentation
|
||||
// time to the next multiple of 4 milliseconds, or coarser.
|
||||
RenderTimeToDOMHighResTimeStamp(paint_timing_info.presentation_time),
|
||||
MonotonicTimeToDOMHighResTimeStamp(
|
||||
paint_timing_info.rendering_update_end_time),
|
||||
DynamicTo<LocalDOMWindow>(GetExecutionContext()),
|
||||
is_triggered_by_soft_navigation);
|
||||
DCHECK((type == PerformancePaintTiming::PaintType::kFirstPaint) ||
|
||||
(type == PerformancePaintTiming::PaintType::kFirstContentfulPaint));
|
||||
|
||||
AddRenderCoarsenedEntry(
|
||||
WTF::BindOnce(
|
||||
[](Persistent<PerformanceEntry> entry, Performance& performance) {
|
||||
if (performance.paint_entries_timing_.size() <
|
||||
kDefaultPaintEntriesBufferSize) {
|
||||
performance.InsertEntryIntoSortedBuffer(
|
||||
performance.paint_entries_timing_, *entry, kRecordSwaps);
|
||||
} else {
|
||||
++(performance.dropped_entries_count_map_
|
||||
.find(PerformanceEntry::kPaint)
|
||||
->value);
|
||||
}
|
||||
performance.NotifyObserversOfEntry(*entry);
|
||||
},
|
||||
WrapPersistent(entry)),
|
||||
entry->startTime());
|
||||
}
|
||||
bool Performance::CanAddResourceTimingEntry() {
|
||||
// https://w3c.github.io/resource-timing/#dfn-can-add-resource-timing-entry
|
||||
return resource_timing_buffer_.size() < resource_timing_buffer_size_limit_;
|
||||
@@ -1410,6 +1303,23 @@ bool Performance::CanExposeNode(Node* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Performance::AddPaintTiming(PerformancePaintTiming::PaintType type,
|
||||
const DOMPaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation) {
|
||||
PerformancePaintTiming* entry = MakeGarbageCollected<PerformancePaintTiming>(
|
||||
type, paint_timing_info, DynamicTo<LocalDOMWindow>(GetExecutionContext()),
|
||||
is_triggered_by_soft_navigation);
|
||||
DCHECK((type == PerformancePaintTiming::PaintType::kFirstPaint) ||
|
||||
(type == PerformancePaintTiming::PaintType::kFirstContentfulPaint));
|
||||
|
||||
if (paint_entries_timing_.size() < kDefaultPaintEntriesBufferSize) {
|
||||
InsertEntryIntoSortedBuffer(paint_entries_timing_, *entry, kRecordSwaps);
|
||||
} else {
|
||||
++(dropped_entries_count_map_.find(PerformanceEntry::kPaint)->value);
|
||||
}
|
||||
NotifyObserversOfEntry(*entry);
|
||||
}
|
||||
|
||||
ScriptObject Performance::toJSONForBinding(ScriptState* script_state) const {
|
||||
V8ObjectBuilder result(script_state);
|
||||
BuildJSONValue(result);
|
||||
|
@@ -72,7 +72,6 @@ class LayoutShift;
|
||||
class MemoryInfo;
|
||||
class MemoryMeasurement;
|
||||
class Node;
|
||||
struct PaintTimingInfo;
|
||||
class PerformanceElementTiming;
|
||||
class PerformanceEventTiming;
|
||||
class PerformanceMark;
|
||||
@@ -211,12 +210,6 @@ class CORE_EXPORT Performance : public EventTarget {
|
||||
|
||||
void NotifyNavigationTimingToObservers();
|
||||
|
||||
void AddFirstPaintTiming(const PaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation);
|
||||
|
||||
void AddFirstContentfulPaintTiming(const PaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation);
|
||||
|
||||
bool IsElementTimingBufferFull() const;
|
||||
void AddToElementTimingBuffer(PerformanceElementTiming&);
|
||||
|
||||
@@ -243,11 +236,6 @@ class CORE_EXPORT Performance : public EventTarget {
|
||||
base::TimeTicks pageshow_start_time,
|
||||
base::TimeTicks pageshow_end_time);
|
||||
|
||||
void AddRenderCoarsenedEntry(
|
||||
base::OnceCallback<void(Performance&)>,
|
||||
DOMHighResTimeStamp earliest_timestamp_for_timeline);
|
||||
void SchedulePendingRenderCoarsenedEntries(base::TimeTicks target_time);
|
||||
void FlushPendingRenderCoarsenedEntries();
|
||||
|
||||
// This enum is used to index different possible strings for for UMA enum
|
||||
// histogram. New enum values can be added, but existing enums must never be
|
||||
@@ -338,15 +326,15 @@ class CORE_EXPORT Performance : public EventTarget {
|
||||
cross_origin_isolated_capability_ = is_isolated;
|
||||
}
|
||||
|
||||
bool CrossOriginIsolatedCapability() const {
|
||||
return cross_origin_isolated_capability_;
|
||||
}
|
||||
|
||||
// TODO(https://crbug.com/1457049): remove this once visited links are
|
||||
// partitioned.
|
||||
bool softNavPaintMetricsSupported() const;
|
||||
|
||||
private:
|
||||
void AddPaintTiming(PerformancePaintTiming::PaintType,
|
||||
const PaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation);
|
||||
|
||||
PerformanceMeasure* MeasureInternal(
|
||||
ScriptState* script_state,
|
||||
const AtomicString& measure_name,
|
||||
@@ -407,7 +395,9 @@ class CORE_EXPORT Performance : public EventTarget {
|
||||
|
||||
virtual void BuildJSONValue(V8ObjectBuilder&) const;
|
||||
|
||||
void AddPendingRenderCoarsenedEntries();
|
||||
void AddPaintTiming(PerformancePaintTiming::PaintType,
|
||||
const DOMPaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation);
|
||||
|
||||
PerformanceEntryVector resource_timing_buffer_;
|
||||
// The secondary RT buffer, used to store incoming entries after the main
|
||||
@@ -454,9 +444,6 @@ class CORE_EXPORT Performance : public EventTarget {
|
||||
// See crbug.com/1181774.
|
||||
Member<BackgroundTracingHelper> background_tracing_helper_;
|
||||
|
||||
Vector<std::pair<base::OnceCallback<void(Performance&)>, base::TimeTicks>>
|
||||
pending_entry_operations_with_render_coarsening_;
|
||||
|
||||
// Running counter for LongTask observations.
|
||||
size_t long_task_counter_ = 0;
|
||||
};
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
|
||||
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
|
||||
#include "third_party/blink/renderer/core/performance_entry_names.h"
|
||||
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/casting.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/uuid.h"
|
||||
|
||||
@@ -163,6 +164,15 @@ String PerformanceEntry::GetNavigationId(ScriptState* script_state) {
|
||||
return local_dom_window->GetNavigationId();
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp PerformanceEntry::paintTime() const {
|
||||
CHECK(RuntimeEnabledFeatures::PaintTimingMixinEnabled());
|
||||
return paint_timing_info_ ? paint_timing_info_->paint_time : 0;
|
||||
}
|
||||
DOMHighResTimeStamp PerformanceEntry::presentationTime() const {
|
||||
CHECK(RuntimeEnabledFeatures::PaintTimingMixinEnabled());
|
||||
return paint_timing_info_ ? paint_timing_info_->presentation_time : 0;
|
||||
}
|
||||
|
||||
void PerformanceEntry::Trace(Visitor* visitor) const {
|
||||
visitor->Trace(source_);
|
||||
ScriptWrappable::Trace(visitor);
|
||||
@@ -184,6 +194,12 @@ void PerformanceEntry::BuildJSONValue(V8ObjectBuilder& builder) const {
|
||||
ExecutionContext::From(builder.GetScriptState()))) {
|
||||
builder.AddString("navigationId", navigationId());
|
||||
}
|
||||
|
||||
if (paint_timing_info_ && RuntimeEnabledFeatures::PaintTimingMixinEnabled()) {
|
||||
builder.AddNumber("paintTime", paint_timing_info_->paint_time);
|
||||
builder.AddNumber("presentationTime",
|
||||
paint_timing_info_->presentation_time);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blink
|
||||
|
@@ -49,6 +49,14 @@ class V8ObjectBuilder;
|
||||
using PerformanceEntryType = unsigned;
|
||||
using PerformanceEntryTypeMask = unsigned;
|
||||
|
||||
struct DOMPaintTimingInfo {
|
||||
// https://w3c.github.io/paint-timing/#paint-timing-info-rendering-update-end-time
|
||||
DOMHighResTimeStamp paint_time;
|
||||
|
||||
// https://w3c.github.io/paint-timing/#paint-timing-info-implementation-defined-presentation-time
|
||||
DOMHighResTimeStamp presentation_time;
|
||||
};
|
||||
|
||||
class CORE_EXPORT PerformanceEntry : public ScriptWrappable {
|
||||
DEFINE_WRAPPERTYPEINFO();
|
||||
|
||||
@@ -107,6 +115,10 @@ class CORE_EXPORT PerformanceEntry : public ScriptWrappable {
|
||||
return true;
|
||||
} else if (b->EntryTypeEnum() == kNavigation) {
|
||||
return false;
|
||||
} else if (a->EntryTypeEnum() == kPaint) {
|
||||
return true;
|
||||
} else if (b->EntryTypeEnum() == kPaint) {
|
||||
return false;
|
||||
} else {
|
||||
return a->index_ < b->index_;
|
||||
}
|
||||
@@ -147,8 +159,18 @@ class CORE_EXPORT PerformanceEntry : public ScriptWrappable {
|
||||
return is_triggered_by_soft_navigation_;
|
||||
}
|
||||
|
||||
// PaintTimingMixin. It's implemented here for simplicity.
|
||||
// If an interface doesn't have PaintTimingMixin, these functions
|
||||
// would not be exposed by WebIDL.
|
||||
DOMHighResTimeStamp paintTime() const;
|
||||
DOMHighResTimeStamp presentationTime() const;
|
||||
|
||||
void Trace(Visitor*) const override;
|
||||
|
||||
void SetPaintTimingInfo(const DOMPaintTimingInfo& paint_timing_info) {
|
||||
paint_timing_info_ = paint_timing_info;
|
||||
}
|
||||
|
||||
protected:
|
||||
PerformanceEntry(const AtomicString& name,
|
||||
double start_time,
|
||||
@@ -174,6 +196,8 @@ class CORE_EXPORT PerformanceEntry : public ScriptWrappable {
|
||||
// source_ will be null if the PerformanceEntry did not originate from a
|
||||
// Window context.
|
||||
const WeakMember<DOMWindow> source_;
|
||||
// For entries implementing PaintTimingMixin.
|
||||
std::optional<DOMPaintTimingInfo> paint_timing_info_;
|
||||
const bool is_triggered_by_soft_navigation_;
|
||||
};
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
|
||||
#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
|
||||
#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
|
||||
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
|
||||
#include "third_party/blink/renderer/core/frame/dom_window.h"
|
||||
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
|
||||
#include "third_party/blink/renderer/core/performance_entry_names.h"
|
||||
@@ -17,21 +18,45 @@
|
||||
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
PerformanceLongAnimationFrameTiming*
|
||||
PerformanceLongAnimationFrameTiming::Create(
|
||||
AnimationFrameTimingInfo* info,
|
||||
base::TimeTicks time_origin,
|
||||
bool cross_origin_isolated_capability,
|
||||
DOMWindow* source,
|
||||
const std::optional<DOMPaintTimingInfo>& paint_timing_info) {
|
||||
Performance* performance =
|
||||
DOMWindowPerformance::performance(*source->ToLocalDOMWindow());
|
||||
DOMHighResTimeStamp startTime =
|
||||
performance->MonotonicTimeToDOMHighResTimeStamp(info->FrameStartTime());
|
||||
double duration = paint_timing_info
|
||||
? (paint_timing_info->paint_time - startTime)
|
||||
: info->Duration().InMillisecondsF();
|
||||
PerformanceLongAnimationFrameTiming* entry =
|
||||
MakeGarbageCollected<PerformanceLongAnimationFrameTiming>(
|
||||
duration, startTime, info, time_origin,
|
||||
cross_origin_isolated_capability, source);
|
||||
if (paint_timing_info.has_value()) {
|
||||
entry->SetPaintTimingInfo(*paint_timing_info);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
PerformanceLongAnimationFrameTiming::PerformanceLongAnimationFrameTiming(
|
||||
double duration,
|
||||
DOMHighResTimeStamp startTime,
|
||||
AnimationFrameTimingInfo* info,
|
||||
base::TimeTicks time_origin,
|
||||
bool cross_origin_isolated_capability,
|
||||
DOMWindow* source)
|
||||
: PerformanceEntry(
|
||||
info->Duration().InMilliseconds(),
|
||||
AtomicString("long-animation-frame"),
|
||||
DOMWindowPerformance::performance(*source->ToLocalDOMWindow())
|
||||
->MonotonicTimeToDOMHighResTimeStamp(info->FrameStartTime()),
|
||||
source) {
|
||||
info_ = info;
|
||||
time_origin_ = time_origin;
|
||||
cross_origin_isolated_capability_ = cross_origin_isolated_capability;
|
||||
}
|
||||
: PerformanceEntry(duration,
|
||||
AtomicString("long-animation-frame"),
|
||||
startTime,
|
||||
source),
|
||||
time_origin_(time_origin),
|
||||
cross_origin_isolated_capability_(cross_origin_isolated_capability),
|
||||
info_(info) {}
|
||||
|
||||
PerformanceLongAnimationFrameTiming::~PerformanceLongAnimationFrameTiming() =
|
||||
default;
|
||||
@@ -94,7 +119,6 @@ DOMHighResTimeStamp PerformanceLongAnimationFrameTiming::blockingDuration()
|
||||
const {
|
||||
return info_->TotalBlockingDuration().InMilliseconds();
|
||||
}
|
||||
|
||||
void PerformanceLongAnimationFrameTiming::BuildJSONValue(
|
||||
V8ObjectBuilder& builder) const {
|
||||
PerformanceEntry::BuildJSONValue(builder);
|
||||
|
@@ -5,6 +5,8 @@
|
||||
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_PERFORMANCE_LONG_ANIMATION_FRAME_TIMING_H_
|
||||
#define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_PERFORMANCE_LONG_ANIMATION_FRAME_TIMING_H_
|
||||
|
||||
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
|
||||
#include "third_party/blink/renderer/core/frame/dom_window.h"
|
||||
#include "third_party/blink/renderer/core/timing/animation_frame_timing_info.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_entry.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_script_timing.h"
|
||||
@@ -19,11 +21,21 @@ class PerformanceLongAnimationFrameTiming final : public PerformanceEntry {
|
||||
public:
|
||||
// This constructor uses int for |duration| to coarsen it in advance.
|
||||
// LongAnimationFrameTiming is always at 1-ms granularity.
|
||||
PerformanceLongAnimationFrameTiming(AnimationFrameTimingInfo* info,
|
||||
|
||||
static PerformanceLongAnimationFrameTiming* Create(
|
||||
AnimationFrameTimingInfo* info,
|
||||
base::TimeTicks time_origin,
|
||||
bool cross_origin_isolated_capability,
|
||||
DOMWindow*,
|
||||
const std::optional<DOMPaintTimingInfo>&);
|
||||
~PerformanceLongAnimationFrameTiming() override;
|
||||
|
||||
PerformanceLongAnimationFrameTiming(double duration,
|
||||
DOMHighResTimeStamp startTime,
|
||||
AnimationFrameTimingInfo* info,
|
||||
base::TimeTicks time_origin,
|
||||
bool cross_origin_isolated_capability,
|
||||
DOMWindow* source);
|
||||
~PerformanceLongAnimationFrameTiming() override;
|
||||
DOMWindow*);
|
||||
|
||||
const AtomicString& entryType() const override;
|
||||
PerformanceEntryType EntryTypeEnum() const override;
|
||||
@@ -37,7 +49,6 @@ class PerformanceLongAnimationFrameTiming final : public PerformanceEntry {
|
||||
const PerformanceScriptVector& scripts() const;
|
||||
|
||||
void Trace(Visitor*) const override;
|
||||
|
||||
private:
|
||||
void BuildJSONValue(V8ObjectBuilder&) const override;
|
||||
DOMHighResTimeStamp ToMonotonicTime(base::TimeTicks) const;
|
||||
|
@@ -13,3 +13,5 @@ interface PerformanceLongAnimationFrameTiming : PerformanceEntry {
|
||||
|
||||
[CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON();
|
||||
};
|
||||
|
||||
PerformanceLongAnimationFrameTiming includes PaintTimingMixin;
|
@@ -5,8 +5,10 @@
|
||||
#include "third_party/blink/renderer/core/timing/performance_paint_timing.h"
|
||||
|
||||
#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
|
||||
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
|
||||
#include "third_party/blink/renderer/core/performance_entry_names.h"
|
||||
#include "third_party/blink/renderer/core/timing/dom_window_performance.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_entry.h"
|
||||
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
|
||||
|
||||
namespace blink {
|
||||
@@ -33,8 +35,7 @@ AtomicString FromPaintTypeToString(PerformancePaintTiming::PaintType type) {
|
||||
|
||||
PerformancePaintTiming::PerformancePaintTiming(
|
||||
PaintType type,
|
||||
DOMHighResTimeStamp start_time,
|
||||
DOMHighResTimeStamp rendering_update_end_time,
|
||||
const DOMPaintTimingInfo& paint_timing_info,
|
||||
DOMWindow* source,
|
||||
bool is_triggered_by_soft_navigation)
|
||||
: PerformanceEntry(
|
||||
@@ -42,11 +43,12 @@ PerformancePaintTiming::PerformancePaintTiming(
|
||||
// https://w3c.github.io/paint-timing/#report-paint-timing
|
||||
// Set newEntry’s startTime attribute to the default paint timestamp
|
||||
// given paintTimingInfo.
|
||||
start_time,
|
||||
start_time,
|
||||
paint_timing_info.presentation_time,
|
||||
paint_timing_info.presentation_time,
|
||||
source,
|
||||
is_triggered_by_soft_navigation),
|
||||
rendering_update_end_time_(rendering_update_end_time) {}
|
||||
is_triggered_by_soft_navigation) {
|
||||
SetPaintTimingInfo(paint_timing_info);
|
||||
}
|
||||
|
||||
PerformancePaintTiming::~PerformancePaintTiming() = default;
|
||||
|
||||
@@ -58,12 +60,4 @@ PerformanceEntryType PerformancePaintTiming::EntryTypeEnum() const {
|
||||
return PerformanceEntry::EntryType::kPaint;
|
||||
}
|
||||
|
||||
void PerformancePaintTiming::BuildJSONValue(V8ObjectBuilder& builder) const {
|
||||
PerformanceEntry::BuildJSONValue(builder);
|
||||
if (RuntimeEnabledFeatures::PaintTimingMixinEnabled()) {
|
||||
builder.AddNumber("paintTime", paintTime());
|
||||
builder.AddNumber("presentationTime", presentationTime());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blink
|
||||
|
@@ -18,22 +18,13 @@ class CORE_EXPORT PerformancePaintTiming final : public PerformanceEntry {
|
||||
enum class PaintType { kFirstPaint, kFirstContentfulPaint };
|
||||
|
||||
PerformancePaintTiming(PaintType,
|
||||
DOMHighResTimeStamp start_time,
|
||||
DOMHighResTimeStamp rendering_update_end_time,
|
||||
const DOMPaintTimingInfo& paint_timing_info,
|
||||
DOMWindow* source,
|
||||
bool is_triggered_by_soft_navigation);
|
||||
~PerformancePaintTiming() override;
|
||||
|
||||
const AtomicString& entryType() const override;
|
||||
PerformanceEntryType EntryTypeEnum() const override;
|
||||
DOMHighResTimeStamp paintTime() const { return rendering_update_end_time_; }
|
||||
DOMHighResTimeStamp presentationTime() const { return startTime(); }
|
||||
|
||||
protected:
|
||||
void BuildJSONValue(V8ObjectBuilder&) const override;
|
||||
|
||||
private:
|
||||
DOMHighResTimeStamp rendering_update_end_time_;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
@@ -36,6 +36,8 @@
|
||||
#include <string>
|
||||
|
||||
#include "base/feature_list.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback_forward.h"
|
||||
#include "base/trace_event/common/trace_event_common.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "base/trace_event/trace_id_helper.h"
|
||||
@@ -79,6 +81,7 @@
|
||||
#include "third_party/blink/renderer/core/timing/performance_event_timing.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_long_animation_frame_timing.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_observer.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_paint_timing.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_timing.h"
|
||||
#include "third_party/blink/renderer/core/timing/performance_timing_for_reporting.h"
|
||||
#include "third_party/blink/renderer/core/timing/responsiveness_metrics.h"
|
||||
@@ -1108,17 +1111,96 @@ bool WindowPerformance::SetInteractionIdAndRecordLatency(
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowPerformance::ReportLongAnimationFrameTiming(
|
||||
AnimationFrameTimingInfo* info) {
|
||||
LocalDOMWindow* window = DomWindow();
|
||||
if (!window) {
|
||||
void WindowPerformance::QueueLongAnimationFrameTiming(
|
||||
AnimationFrameTimingInfo* info,
|
||||
std::optional<DOMPaintTimingInfo> paint_timing_info) {
|
||||
if (auto* window = DomWindow()) {
|
||||
AddLongAnimationFrameEntry(PerformanceLongAnimationFrameTiming::Create(
|
||||
info, time_origin_, cross_origin_isolated_capability_, window,
|
||||
paint_timing_info));
|
||||
}
|
||||
}
|
||||
|
||||
void WindowPerformance::AddFirstPaintTiming(
|
||||
const DOMPaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation) {
|
||||
AddPaintTiming(PerformancePaintTiming::PaintType::kFirstPaint,
|
||||
paint_timing_info, is_triggered_by_soft_navigation);
|
||||
}
|
||||
|
||||
void WindowPerformance::AddFirstContentfulPaintTiming(
|
||||
const DOMPaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation) {
|
||||
AddPaintTiming(PerformancePaintTiming::PaintType::kFirstContentfulPaint,
|
||||
paint_timing_info, is_triggered_by_soft_navigation);
|
||||
}
|
||||
|
||||
void WindowPerformance::QueueEntryWithPaintTiming(
|
||||
base::OnceCallback<void(WindowPerformance*, const DOMPaintTimingInfo&)>
|
||||
callback,
|
||||
const DOMPaintTimingInfo& paint_timing_info) {
|
||||
if (!RuntimeEnabledFeatures::ExposeCoarsenedRenderTimeEnabled() ||
|
||||
time_origin_.is_null() || cross_origin_isolated_capability_) {
|
||||
std::move(callback).Run(this, paint_timing_info);
|
||||
return;
|
||||
}
|
||||
|
||||
PerformanceLongAnimationFrameTiming* entry =
|
||||
MakeGarbageCollected<PerformanceLongAnimationFrameTiming>(
|
||||
info, time_origin_, cross_origin_isolated_capability_, window);
|
||||
// https://w3c.github.io/paint-timing/#mark-paint-timing
|
||||
// 10.3.2 Wait until the current high resolution time is paintTimingInfo’s
|
||||
// implementation-defined presentation time.
|
||||
// |target_time| here is using the coarsened time in DOMPaintTimingInfo, and
|
||||
// adds it to the time origin to create a new target time relative to the
|
||||
// shared monotonic clock.
|
||||
base::TimeTicks target_time =
|
||||
time_origin_ + base::Milliseconds(paint_timing_info.presentation_time);
|
||||
if (pending_entry_operations_with_render_coarsening_.empty()) {
|
||||
SchedulePendingRenderCoarsenedEntries(target_time);
|
||||
}
|
||||
|
||||
pending_entry_operations_with_render_coarsening_.push_back(
|
||||
std::make_pair(WTF::BindOnce(std::move(callback),
|
||||
WrapWeakPersistent(this), paint_timing_info),
|
||||
target_time));
|
||||
}
|
||||
|
||||
void WindowPerformance::SchedulePendingRenderCoarsenedEntries(
|
||||
base::TimeTicks target_time) {
|
||||
task_runner_->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
WTF::BindOnce(
|
||||
[](WeakPersistent<WindowPerformance> self) {
|
||||
if (self) {
|
||||
self->FlushPendingRenderCoarsenedEntries();
|
||||
}
|
||||
},
|
||||
WrapWeakPersistent(this)),
|
||||
target_time - base::TimeTicks::Now());
|
||||
}
|
||||
|
||||
void WindowPerformance::FlushPendingRenderCoarsenedEntries() {
|
||||
base::TimeTicks now = base::TimeTicks::Now();
|
||||
|
||||
Vector<std::pair<base::OnceClosure, base::TimeTicks>> pending_entries;
|
||||
std::swap(pending_entry_operations_with_render_coarsening_, pending_entries);
|
||||
base::TimeTicks next_tick;
|
||||
for (auto& [callback, target_time] : pending_entries) {
|
||||
// We could have had a few entries batched and this one is coarsened to the
|
||||
// future. Fire it in the next batch.
|
||||
if (target_time > now) {
|
||||
pending_entry_operations_with_render_coarsening_.push_back(
|
||||
std::make_pair(std::move(callback), target_time));
|
||||
next_tick =
|
||||
next_tick.is_null() ? target_time : std::min(next_tick, target_time);
|
||||
} else {
|
||||
std::move(callback).Run();
|
||||
}
|
||||
}
|
||||
|
||||
if (!next_tick.is_null()) {
|
||||
SchedulePendingRenderCoarsenedEntries(next_tick);
|
||||
}
|
||||
}
|
||||
void WindowPerformance::AddLongAnimationFrameEntry(PerformanceEntry* entry) {
|
||||
if (!IsLongAnimationFrameBufferFull()) {
|
||||
InsertEntryIntoSortedBuffer(long_animation_frame_buffer_, *entry,
|
||||
kRecordSwaps);
|
||||
@@ -1143,31 +1225,33 @@ void WindowPerformance::AddElementTiming(const AtomicString& name,
|
||||
DOMHighResTimeStamp coarsened_load_time =
|
||||
MonotonicTimeToDOMHighResTimeStamp(load_time);
|
||||
|
||||
DOMHighResTimeStamp coarsened_render_time =
|
||||
RenderTimeToDOMHighResTimeStamp(start_time);
|
||||
DOMPaintTimingInfo paint_timing_info{
|
||||
// TODO: integrate PaintTimingMixin with element timing
|
||||
.paint_time = RenderTimeToDOMHighResTimeStamp(start_time),
|
||||
.presentation_time = RenderTimeToDOMHighResTimeStamp(start_time),
|
||||
};
|
||||
|
||||
PerformanceElementTiming* entry = PerformanceElementTiming::Create(
|
||||
name, url, rect, coarsened_render_time, coarsened_load_time, identifier,
|
||||
intrinsic_size.width(), intrinsic_size.height(), id, element,
|
||||
name, url, rect, paint_timing_info.presentation_time, coarsened_load_time,
|
||||
identifier, intrinsic_size.width(), intrinsic_size.height(), id, element,
|
||||
DomWindow());
|
||||
TRACE_EVENT2("loading", "PerformanceElementTiming", "data",
|
||||
entry->ToTracedValue(), "frame",
|
||||
GetFrameIdForTracing(DomWindow()->GetFrame()));
|
||||
|
||||
AddRenderCoarsenedEntry(
|
||||
QueueEntryWithPaintTiming(
|
||||
WTF::BindOnce(
|
||||
[](Persistent<PerformanceElementTiming> entry,
|
||||
Performance& performance) {
|
||||
if (performance.HasObserverFor(PerformanceEntry::kElement)) {
|
||||
static_cast<WindowPerformance&>(performance)
|
||||
.NotifyObserversOfEntry(*entry);
|
||||
WindowPerformance* performance, const DOMPaintTimingInfo&) {
|
||||
if (performance->HasObserverFor(PerformanceEntry::kElement)) {
|
||||
performance->NotifyObserversOfEntry(*entry);
|
||||
}
|
||||
if (!performance.IsElementTimingBufferFull()) {
|
||||
performance.AddToElementTimingBuffer(*entry);
|
||||
if (!performance->IsElementTimingBufferFull()) {
|
||||
performance->AddToElementTimingBuffer(*entry);
|
||||
}
|
||||
},
|
||||
WrapPersistent(entry)),
|
||||
coarsened_render_time);
|
||||
paint_timing_info);
|
||||
}
|
||||
|
||||
void WindowPerformance::DispatchFirstInputTiming(
|
||||
@@ -1282,33 +1366,35 @@ void WindowPerformance::OnLargestContentfulPaintUpdated(
|
||||
DOMHighResTimeStamp first_animated_frame_timestamp =
|
||||
RenderTimeToDOMHighResTimeStamp(first_animated_frame_time);
|
||||
|
||||
// TODO(crbug.com/381270287) integrate with PaintMixin. This currently doesn't
|
||||
// have a proper paint_time.
|
||||
DOMPaintTimingInfo paint_timing_info{render_timestamp, render_timestamp};
|
||||
|
||||
// TODO(yoav): Should we modify start to represent the animated frame?
|
||||
auto* entry = MakeGarbageCollected<LargestContentfulPaint>(
|
||||
start_timestamp, render_timestamp, paint_size, load_timestamp,
|
||||
first_animated_frame_timestamp, id, url, element, DomWindow(),
|
||||
is_triggered_by_soft_navigation);
|
||||
|
||||
AddRenderCoarsenedEntry(
|
||||
QueueEntryWithPaintTiming(
|
||||
WTF::BindOnce(
|
||||
[](Persistent<LargestContentfulPaint> entry,
|
||||
Performance& performance) {
|
||||
WindowPerformance& window_performance =
|
||||
static_cast<WindowPerformance&>(performance);
|
||||
if (!window_performance.DomWindow()) {
|
||||
WindowPerformance* window_performance, const DOMPaintTimingInfo&) {
|
||||
if (!window_performance->DomWindow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (performance.HasObserverFor(
|
||||
if (window_performance->HasObserverFor(
|
||||
PerformanceEntry::kLargestContentfulPaint)) {
|
||||
window_performance.NotifyObserversOfEntry(*entry);
|
||||
window_performance->NotifyObserversOfEntry(*entry);
|
||||
}
|
||||
performance.AddLargestContentfulPaint(entry);
|
||||
window_performance.DomWindow()
|
||||
window_performance->AddLargestContentfulPaint(entry);
|
||||
window_performance->DomWindow()
|
||||
->document()
|
||||
->OnLargestContentfulPaintUpdated();
|
||||
},
|
||||
WrapPersistent(entry)),
|
||||
render_timestamp);
|
||||
paint_timing_info);
|
||||
|
||||
if (HTMLImageElement* image_element = DynamicTo<HTMLImageElement>(element)) {
|
||||
image_element->SetIsLCPElement();
|
||||
|
@@ -28,7 +28,6 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_WINDOW_PERFORMANCE_H_
|
||||
#define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_WINDOW_PERFORMANCE_H_
|
||||
|
||||
@@ -127,7 +126,16 @@ class CORE_EXPORT WindowPerformance final : public Performance,
|
||||
Element*);
|
||||
|
||||
void OnBodyLoadFinished(int64_t encoded_body_size, int64_t decoded_body_size);
|
||||
void ReportLongAnimationFrameTiming(AnimationFrameTimingInfo*);
|
||||
void QueueLongAnimationFrameTiming(
|
||||
AnimationFrameTimingInfo*,
|
||||
std::optional<DOMPaintTimingInfo> paint_timing_info = std::nullopt);
|
||||
void AddFirstPaintTiming(const DOMPaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation);
|
||||
|
||||
void AddFirstContentfulPaintTiming(
|
||||
const DOMPaintTimingInfo& paint_timing_info,
|
||||
bool is_triggered_by_soft_navigation);
|
||||
|
||||
// PerformanceMonitor::Client implementation.
|
||||
void ReportLongTask(base::TimeTicks start_time,
|
||||
base::TimeTicks end_time,
|
||||
@@ -171,6 +179,10 @@ class CORE_EXPORT WindowPerformance final : public Performance,
|
||||
void OnPageScroll();
|
||||
bool IsAutoscrollActive();
|
||||
void ResetAutoscroll() { autoscroll_active_ = false; }
|
||||
void QueueEntryWithPaintTiming(
|
||||
base::OnceCallback<void(WindowPerformance*, const DOMPaintTimingInfo&)>
|
||||
callback,
|
||||
const DOMPaintTimingInfo&);
|
||||
|
||||
private:
|
||||
static std::pair<AtomicString, DOMWindow*> SanitizedAttribution(
|
||||
@@ -183,6 +195,7 @@ class CORE_EXPORT WindowPerformance final : public Performance,
|
||||
void ReportAllPendingEventTimingsOnPageHidden();
|
||||
|
||||
void FlushEventTimingsOnPageHidden();
|
||||
void AddLongAnimationFrameEntry(PerformanceEntry*);
|
||||
|
||||
void OnPresentationPromiseResolved(
|
||||
uint64_t presentation_index,
|
||||
@@ -208,6 +221,9 @@ class CORE_EXPORT WindowPerformance final : public Performance,
|
||||
|
||||
void ReportFirstInputTiming(PerformanceEventTiming* event_timing_entry);
|
||||
|
||||
void SchedulePendingRenderCoarsenedEntries(base::TimeTicks target_time);
|
||||
void FlushPendingRenderCoarsenedEntries();
|
||||
|
||||
// The last time the page visibility was changed.
|
||||
base::TimeTicks last_hidden_timestamp_;
|
||||
|
||||
@@ -254,6 +270,9 @@ class CORE_EXPORT WindowPerformance final : public Performance,
|
||||
Member<ResponsivenessMetrics> responsiveness_metrics_;
|
||||
// The event we are currently processing.
|
||||
WeakMember<const Event> current_event_;
|
||||
|
||||
Vector<std::pair<base::OnceClosure, base::TimeTicks>>
|
||||
pending_entry_operations_with_render_coarsening_;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
@@ -50,6 +50,7 @@ struct FrameTimingDetails;
|
||||
|
||||
namespace blink {
|
||||
|
||||
class AnimationFrameTimingInfo;
|
||||
class LocalFrame;
|
||||
// In interface exposed within Blink from local root frames that provides
|
||||
// local-root specific things related to compositing and input. This
|
||||
@@ -324,7 +325,8 @@ class PLATFORM_EXPORT FrameWidget {
|
||||
// Implementation of
|
||||
// https://w3c.github.io/long-animation-frames/#record-rendering-time (the
|
||||
// other parameters are recorded earlier).
|
||||
virtual void RecordRenderingUpdateEndTime(base::TimeTicks) = 0;
|
||||
virtual AnimationFrameTimingInfo* RecordRenderingUpdateEndTime(
|
||||
base::TimeTicks) = 0;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
2
third_party/blink/web_tests/external/wpt/largest-contentful-paint/first-paint-equals-lcp-text.html
vendored
2
third_party/blink/web_tests/external/wpt/largest-contentful-paint/first-paint-equals-lcp-text.html
vendored
@@ -14,7 +14,7 @@
|
||||
let largestContentfulPaintTime = 0;
|
||||
const observer = new PerformanceObserver(
|
||||
t.step_func(function(entryList) {
|
||||
entryList.getEntries().forEach(entry => {
|
||||
entryList.getEntries().forEach(entry => {
|
||||
if (entry.name === 'first-paint') {
|
||||
assert_equals(firstPaintTime, 0, 'Only one first-paint entry.');
|
||||
assert_equals(entry.entryType, 'paint');
|
||||
|
22
third_party/blink/web_tests/external/wpt/long-animation-frame/tentative/loaf-paint-mixin.html
vendored
Normal file
22
third_party/blink/web_tests/external/wpt/long-animation-frame/tentative/loaf-paint-mixin.html
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE HTML>
|
||||
<meta charset=utf-8>
|
||||
<title>Long Animation Frame Timing: paint mixin</title>
|
||||
<meta name="timeout" content="long">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/utils.js"></script>
|
||||
|
||||
<body>
|
||||
<h1>Long Animation Frame: paint mixin</h1>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
promise_test(async t => {
|
||||
assert_implements("paintTime" in window.PerformanceLongAnimationFrameTiming.prototype, "PerformanceLongAnimationFrameTiming doesn't expose `paintTime`");
|
||||
document.querySelector("#log").innerText = "Text";
|
||||
const entry = await generate_long_animation_frame();
|
||||
assert_equals(entry.paintTime, entry.startTime + entry.duration);
|
||||
assert_greater_than(entry.presentationTime, entry.paintTime);
|
||||
}, 'A LoAF that generates a frame should have paintTime/presentationTime');
|
||||
</script>
|
||||
</body>
|
5
third_party/blink/web_tests/external/wpt/long-animation-frame/tentative/loaf-timeline.html
vendored
5
third_party/blink/web_tests/external/wpt/long-animation-frame/tentative/loaf-timeline.html
vendored
@@ -14,14 +14,13 @@ promise_test(async t => {
|
||||
busy_wait(very_long_frame_duration);
|
||||
const is_loaf = entry => entry.duration >= very_long_frame_duration &&
|
||||
entry.entryType == "long-animation-frame";
|
||||
|
||||
await new Promise(resolve => t.step_timeout(resolve, 10));
|
||||
await new Promise(resolve => t.step_timeout(resolve, 300));
|
||||
const entry_from_all = [...performance.getEntries()].find(is_loaf);
|
||||
const entry_by_type = [...performance.getEntriesByType("long-animation-frame")].find(is_loaf);
|
||||
const entry_by_name = [...performance.getEntriesByName("long-animation-frame")].find(is_loaf);
|
||||
assert_true(!!entry_from_all, "LoAF Entry found");
|
||||
assert_equals(entry_from_all, entry_by_type);
|
||||
assert_equals(entry_from_all, entry_by_name);
|
||||
}, 'LoAF entries are available in the performnace timeline');
|
||||
}, 'LoAF entries are available in the performance timeline');
|
||||
</script>
|
||||
</body>
|
||||
|
19
third_party/blink/web_tests/external/wpt/long-animation-frame/tentative/resources/utils.js
vendored
19
third_party/blink/web_tests/external/wpt/long-animation-frame/tentative/resources/utils.js
vendored
@@ -52,8 +52,8 @@ async function expect_long_frame(cb, t) {
|
||||
t.step_timeout(() => resolve("timeout"), waiting_for_long_frame_timeout));
|
||||
let resolve_loaf;
|
||||
const received_loaf = new Promise(resolve => { resolve_loaf = resolve; });
|
||||
const generate_loaf = () =>
|
||||
generate_long_animation_frame(very_long_frame_duration).then(resolve_loaf);
|
||||
const generate_loaf = (duration = very_long_frame_duration) =>
|
||||
generate_long_animation_frame(duration).then(resolve_loaf);
|
||||
window.generate_loaf_now = generate_loaf;
|
||||
await cb(t, generate_loaf);
|
||||
const entry = await Promise.race([
|
||||
@@ -64,6 +64,21 @@ async function expect_long_frame(cb, t) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
function generate_long_animation_frame(duration = 120) {
|
||||
busy_wait(duration / 2);
|
||||
const reference_time = performance.now();
|
||||
busy_wait(duration / 2);
|
||||
return new Promise(resolve => new PerformanceObserver((entries, observer) => {
|
||||
const entry = entries.getEntries().find(e =>
|
||||
(e.startTime < reference_time) &&
|
||||
(reference_time < (e.startTime + e.duration)));
|
||||
if (entry) {
|
||||
observer.disconnect();
|
||||
resolve(entry);
|
||||
}
|
||||
}).observe({type: "long-animation-frame"}));
|
||||
}
|
||||
|
||||
async function expect_long_frame_with_script(cb, predicate, t) {
|
||||
const entry = await expect_long_frame(cb, t);
|
||||
for (const script of entry.scripts ?? []) {
|
||||
|
@@ -24,7 +24,6 @@
|
||||
resolve(entry);
|
||||
}).observe({type: "paint"});
|
||||
});
|
||||
await new Promise(resolve => img.addEventListener("load", () => resolve()));
|
||||
const entry = await performance_entry_promise;
|
||||
assert_greater_than(entry.paintTime, reference_time);
|
||||
assert_greater_than(entry.presentationTime, entry.paintTime);
|
||||
|
@@ -7224,6 +7224,8 @@ interface PerformanceLongAnimationFrameTiming : PerformanceEntry
|
||||
attribute @@toStringTag
|
||||
getter blockingDuration
|
||||
getter firstUIEventTimestamp
|
||||
getter paintTime
|
||||
getter presentationTime
|
||||
getter renderStart
|
||||
getter scripts
|
||||
getter styleAndLayoutStart
|
||||
|
Reference in New Issue
Block a user