Bug 1862939 - Part 1: Add LCP to nsDOMNavigationTiming. r=sefeng

Differential Revision: https://phabricator.services.mozilla.com/D192689
This commit is contained in:
Bas Schouten 2023-11-06 20:32:43 +00:00
parent f0bf641c7b
commit c5c5d07e9f
5 changed files with 90 additions and 28 deletions

View File

@ -57,6 +57,7 @@ void nsDOMNavigationTiming::Clear() {
mDOMContentLoadedEventEnd = TimeStamp();
mDOMComplete = TimeStamp();
mContentfulComposite = TimeStamp();
mLargestContentfulRender = TimeStamp();
mNonBlankPaint = TimeStamp();
mDocShellHasBeenActiveSinceNavigationStart = false;
@ -478,6 +479,16 @@ void nsDOMNavigationTiming::NotifyContentfulCompositeForRootContentDocument(
}
}
void nsDOMNavigationTiming::NotifyLargestContentfulRenderForRootContentDocument(
const DOMHighResTimeStamp& aRenderTime) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mNavigationStart.IsNull());
// This can get called multiple times and updates over time.
mLargestContentfulRender =
mNavigationStart + TimeDuration::FromMilliseconds(aRenderTime);
}
void nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mNavigationStart.IsNull());

View File

@ -71,6 +71,10 @@ class nsDOMNavigationTiming final : public mozilla::RelativeTimeline {
return mContentfulComposite;
}
mozilla::TimeStamp GetLargestContentfulRenderTimeStamp() const {
return mLargestContentfulRender;
}
DOMTimeMilliSec GetUnloadEventStart() {
return TimeStampToDOM(GetUnloadEventStartTimeStamp());
}
@ -104,6 +108,9 @@ class nsDOMNavigationTiming final : public mozilla::RelativeTimeline {
DOMTimeMilliSec GetTimeToContentfulComposite() const {
return TimeStampToDOM(mContentfulComposite);
}
DOMTimeMilliSec GetTimeToLargestContentfulRender() const {
return TimeStampToDOM(mLargestContentfulRender);
}
DOMTimeMilliSec GetTimeToTTFI() const { return TimeStampToDOM(mTTFI); }
DOMTimeMilliSec GetTimeToDOMContentFlushed() const {
return TimeStampToDOM(mDOMContentFlushed);
@ -172,6 +179,8 @@ class nsDOMNavigationTiming final : public mozilla::RelativeTimeline {
void NotifyNonBlankPaintForRootContentDocument();
void NotifyContentfulCompositeForRootContentDocument(
const mozilla::TimeStamp& aCompositeEndTime);
void NotifyLargestContentfulRenderForRootContentDocument(
const DOMHighResTimeStamp& aRenderTime);
void NotifyDOMContentFlushedForRootContentDocument();
void NotifyDocShellStateChanged(DocShellState aDocShellState);
@ -230,6 +239,7 @@ class nsDOMNavigationTiming final : public mozilla::RelativeTimeline {
mozilla::TimeStamp mNavigationStart;
mozilla::TimeStamp mNonBlankPaint;
mozilla::TimeStamp mContentfulComposite;
mozilla::TimeStamp mLargestContentfulRender;
mozilla::TimeStamp mDOMContentFlushed;
mozilla::TimeStamp mBeforeUnloadStart;

View File

@ -45,7 +45,7 @@ perf:
unit: ms
fcp_time:
description:
"Time between firstContentfulPaint and naviationStart, in ms."
"Time between firstContentfulPaint and navigationStart, in ms."
type: quantity
unit: ms
js_exec_time:

View File

@ -12,6 +12,7 @@
#include "PerformanceMainThread.h"
#include "LargestContentfulPaint.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/DOMIntersectionObserver.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"
@ -54,12 +55,14 @@ LargestContentfulPaint::LargestContentfulPaint(
PerformanceMainThread* aPerformance, const DOMHighResTimeStamp aRenderTime,
const DOMHighResTimeStamp aLoadTime, const unsigned long aSize,
nsIURI* aURI, Element* aElement,
const Maybe<const LCPImageEntryKey>& aLCPImageEntryKey)
const Maybe<const LCPImageEntryKey>& aLCPImageEntryKey,
bool aShouldExposeRenderTime)
: PerformanceEntry(aPerformance->GetParentObject(), u""_ns,
kLargestContentfulPaintName),
mPerformance(aPerformance),
mRenderTime(aRenderTime),
mLoadTime(aLoadTime),
mShouldExposeRenderTime(aShouldExposeRenderTime),
mSize(aSize),
mURI(aURI),
mElement(aElement),
@ -187,24 +190,11 @@ void LargestContentfulPaint::MaybeProcessImageForElementTiming(
DOMHighResTimeStamp nowTime =
performance->TimeStampToDOMHighResForRendering(TimeStamp::Now());
if (!request->IsData() && !request->ShouldReportRenderTimeForLCP()) {
// https://wicg.github.io/element-timing/#report-image-element-timing
LOG(" Added a pending image rendering (TAO FAILED)");
// For TAO failed requests, the renderTime is exposed as 0 for
// security reasons.
LCPHelpers::CreateLCPEntryForImage(performance, aElement, aRequest, nowTime,
0 /* aRenderTime */, entryKey);
return;
}
// Otherwise, add the triple (element, imageRequest, now) to roots images
// pending rendering.
//
// At this point, the loadTime of the image is known, but
// the renderTime is unknown, so it's added to ImagesPendingRendering
// as a placeholder, and the corresponding LCP entry will be created
// when the renderTime is known.
LOG(" Added a pending image rendering (TAO PASSED)");
LOG(" Added a pending image rendering");
performance->AddImagesPendingRendering(
ImagePendingRendering{entryKey, nowTime});
}
@ -270,6 +260,9 @@ void LCPHelpers::FinalizeLCPEntryForImage(
}
DOMHighResTimeStamp LargestContentfulPaint::RenderTime() const {
if (!mShouldExposeRenderTime) {
return 0;
}
return nsRFPService::ReduceTimePrecisionAsMSecs(
mRenderTime, mPerformance->GetRandomTimelineSeed(),
mPerformance->GetRTPCallerType());
@ -282,7 +275,8 @@ DOMHighResTimeStamp LargestContentfulPaint::LoadTime() const {
}
DOMHighResTimeStamp LargestContentfulPaint::StartTime() const {
DOMHighResTimeStamp startTime = !mRenderTime ? mLoadTime : mRenderTime;
DOMHighResTimeStamp startTime =
mShouldExposeRenderTime ? mRenderTime : mLoadTime;
return nsRFPService::ReduceTimePrecisionAsMSecs(
startTime, mPerformance->GetRandomTimelineSeed(),
mPerformance->GetRTPCallerType());
@ -299,6 +293,8 @@ Element* LargestContentfulPaint::GetContainingBlockForTextFrame(
void LargestContentfulPaint::QueueEntry() {
LOG("QueueEntry entry=%p", this);
mPerformance->QueueLargestContentfulPaintEntry(this);
ReportLCPToNavigationTimings();
}
void LargestContentfulPaint::GetUrl(nsAString& aUrl) {
@ -469,11 +465,20 @@ void LCPHelpers::CreateLCPEntryForImage(
nsCOMPtr<nsIURI> requestURI;
aRequestProxy->GetURI(getter_AddRefs(requestURI));
imgRequest* request = aRequestProxy->GetOwner();
// We should never get here unless request is valid.
MOZ_ASSERT(request);
bool taoPassed = request->IsData() || request->ShouldReportRenderTimeForLCP();
// https://wicg.github.io/element-timing/#report-image-element-timing
// For TAO failed requests, the renderTime is exposed as 0 for
// security reasons.
//
// At this point, we have all the information about the entry
// except the size.
RefPtr<LargestContentfulPaint> entry =
new LargestContentfulPaint(aPerformance, aRenderTime, aLoadTime, 0,
requestURI, aElement, Some(aImageEntryKey));
RefPtr<LargestContentfulPaint> entry = new LargestContentfulPaint(
aPerformance, aRenderTime, aLoadTime, 0, requestURI, aElement,
Some(aImageEntryKey), taoPassed);
LOG(" Upsert a LargestContentfulPaint entry=%p to LCPEntryMap.",
entry.get());
@ -496,8 +501,9 @@ void LCPHelpers::FinalizeLCPEntryForText(
aContainingBlock->SetFlags(ELEMENT_PROCESSED_BY_LCP_FOR_TEXT);
RefPtr<LargestContentfulPaint> entry = new LargestContentfulPaint(
aPerformance, aRenderTime, 0, 0, nullptr, aContainingBlock, Nothing());
RefPtr<LargestContentfulPaint> entry =
new LargestContentfulPaint(aPerformance, aRenderTime, 0, 0, nullptr,
aContainingBlock, Nothing(), true);
entry->UpdateSize(aContainingBlock, aTargetRectRelativeToSelf, aPerformance,
false);
@ -512,4 +518,31 @@ void LCPHelpers::FinalizeLCPEntryForText(
}
entry->QueueEntry();
}
void LargestContentfulPaint::ReportLCPToNavigationTimings() {
const Document* document = mElement->OwnerDoc();
MOZ_ASSERT(document);
nsDOMNavigationTiming* timing = document->GetNavigationTiming();
if (MOZ_UNLIKELY(!timing)) {
return;
}
if (document->IsResourceDoc()) {
return;
}
if (BrowsingContext* browsingContext = document->GetBrowsingContext()) {
if (browsingContext->GetEmbeddedInContentDocument()) {
return;
}
}
if (!document->IsTopLevelContentDocument()) {
return;
}
timing->NotifyLargestContentfulRenderForRootContentDocument(mRenderTime);
}
} // namespace mozilla::dom

View File

@ -168,12 +168,13 @@ class LargestContentfulPaint final : public PerformanceEntry {
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LargestContentfulPaint,
PerformanceEntry)
LargestContentfulPaint(
PerformanceMainThread* aPerformance,
const DOMHighResTimeStamp aRenderTime,
const DOMHighResTimeStamp aLoadTime, const unsigned long aSize,
nsIURI* aURI, Element* aElement,
const Maybe<const LCPImageEntryKey>& aLCPImageEntryKey);
LargestContentfulPaint(PerformanceMainThread* aPerformance,
const DOMHighResTimeStamp aRenderTime,
const DOMHighResTimeStamp aLoadTime,
const unsigned long aSize, nsIURI* aURI,
Element* aElement,
const Maybe<const LCPImageEntryKey>& aLCPImageEntryKey,
bool aShouldExposeRenderTime);
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
@ -212,10 +213,17 @@ class LargestContentfulPaint final : public PerformanceEntry {
private:
~LargestContentfulPaint() = default;
void ReportLCPToNavigationTimings();
RefPtr<PerformanceMainThread> mPerformance;
// This is always set but only exposed to web content if
// mShouldExposeRenderTime is true.
DOMHighResTimeStamp mRenderTime;
DOMHighResTimeStamp mLoadTime;
// This is set to false when for security reasons web content it not allowed
// to see the RenderTime.
const bool mShouldExposeRenderTime;
unsigned long mSize;
nsCOMPtr<nsIURI> mURI;