Bug 1808323 - Add telemetry for the page load time and the time to first paint for sites that supports Early Hints response, r=manuel,necko-reviewers,valentin

Differential Revision: https://phabricator.services.mozilla.com/D165966
This commit is contained in:
Kershaw Chang 2023-05-02 08:08:44 +00:00
parent e936789b18
commit 8f95e3fca6
23 changed files with 132 additions and 23 deletions

View file

@ -2108,6 +2108,7 @@ void Document::AccumulatePageLoadTelemetry(
nsAutoCString dnsKey("Native"); nsAutoCString dnsKey("Native");
nsAutoCString http3Key; nsAutoCString http3Key;
nsAutoCString http3WithPriorityKey; nsAutoCString http3WithPriorityKey;
nsAutoCString earlyHintKey;
nsCOMPtr<nsIHttpChannelInternal> httpChannel = nsCOMPtr<nsIHttpChannelInternal> httpChannel =
do_QueryInterface(GetChannel()); do_QueryInterface(GetChannel());
if (httpChannel) { if (httpChannel) {
@ -2147,6 +2148,16 @@ void Document::AccumulatePageLoadTelemetry(
aEventTelemetryDataOut.httpVer = mozilla::Some(major); aEventTelemetryDataOut.httpVer = mozilla::Some(major);
} }
uint32_t earlyHintType = 0;
Unused << httpChannel->GetEarlyHintLinkType(&earlyHintType);
if (earlyHintType & LinkStyle::ePRECONNECT) {
earlyHintKey.Append("preconnect_"_ns);
}
if (earlyHintType & LinkStyle::ePRELOAD) {
earlyHintKey.Append("preload_"_ns);
earlyHintKey.Append(mPreloadService.GetEarlyHintUsed() ? "1"_ns : "0"_ns);
}
} }
TimeStamp asyncOpen; TimeStamp asyncOpen;
@ -2174,6 +2185,12 @@ void Document::AccumulatePageLoadTelemetry(
navigationStart, firstContentfulComposite); navigationStart, firstContentfulComposite);
} }
if (!earlyHintKey.IsEmpty()) {
Telemetry::AccumulateTimeDelta(
Telemetry::EH_PERF_FIRST_CONTENTFUL_PAINT_MS, earlyHintKey,
navigationStart, firstContentfulComposite);
}
Telemetry::AccumulateTimeDelta( Telemetry::AccumulateTimeDelta(
Telemetry::DNS_PERF_FIRST_CONTENTFUL_PAINT_MS, dnsKey, navigationStart, Telemetry::DNS_PERF_FIRST_CONTENTFUL_PAINT_MS, dnsKey, navigationStart,
firstContentfulComposite); firstContentfulComposite);
@ -2212,6 +2229,12 @@ void Document::AccumulatePageLoadTelemetry(
loadEventStart); loadEventStart);
} }
if (!earlyHintKey.IsEmpty()) {
Telemetry::AccumulateTimeDelta(Telemetry::EH_PERF_PAGE_LOAD_TIME_MS,
earlyHintKey, navigationStart,
loadEventStart);
}
Telemetry::AccumulateTimeDelta( Telemetry::AccumulateTimeDelta(
Telemetry::PERF_PAGE_LOAD_TIME_FROM_RESPONSESTART_MS, responseStart, Telemetry::PERF_PAGE_LOAD_TIME_FROM_RESPONSESTART_MS, responseStart,
loadEventStart); loadEventStart);

View file

@ -519,7 +519,7 @@ nsresult FetchDriver::HttpFetch(
RefPtr<PreloaderBase> fetchPreload = FindPreload(uri); RefPtr<PreloaderBase> fetchPreload = FindPreload(uri);
if (fetchPreload) { if (fetchPreload) {
fetchPreload->RemoveSelf(mDocument); fetchPreload->RemoveSelf(mDocument);
fetchPreload->NotifyUsage(PreloaderBase::LoadBackground::Keep); fetchPreload->NotifyUsage(mDocument, PreloaderBase::LoadBackground::Keep);
rv = fetchPreload->AsyncConsume(this); rv = fetchPreload->AsyncConsume(this);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {

View file

@ -3609,6 +3609,7 @@ mozilla::ipc::IPCResult ContentChild::RecvCrossProcessRedirect(
if (RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(newChannel)) { if (RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(newChannel)) {
httpChannel->SetEarlyHints(std::move(aArgs.earlyHints())); httpChannel->SetEarlyHints(std::move(aArgs.earlyHints()));
httpChannel->SetEarlyHintLinkType(aArgs.earlyHintLinkType());
} }
// This is used to report any errors back to the parent by calling // This is used to report any errors back to the parent by calling

View file

@ -1370,7 +1370,7 @@ ScriptLoadRequest* ScriptLoader::LookupPreloadRequest(
// This makes sure the pending preload (if exists) for this resource is // This makes sure the pending preload (if exists) for this resource is
// properly marked as used and thus not notified in the console as unused. // properly marked as used and thus not notified in the console as unused.
request->GetScriptLoadContext()->NotifyUsage(); request->GetScriptLoadContext()->NotifyUsage(mDocument);
// A used preload must no longer be found in the Document's hash table. Any // A used preload must no longer be found in the Document's hash table. Any
// <link preload> tag after the <script> tag will start a new request, that // <link preload> tag after the <script> tag will start a new request, that
// can be satisfied from a different cache, but not from the preload cache. // can be satisfied from a different cache, but not from the preload cache.

View file

@ -2756,7 +2756,7 @@ already_AddRefed<PreloaderBase> XMLHttpRequestMainThread::FindPreload() {
} }
preload->RemoveSelf(doc); preload->RemoveSelf(doc);
preload->NotifyUsage(PreloaderBase::LoadBackground::Keep); preload->NotifyUsage(doc, PreloaderBase::LoadBackground::Keep);
return preload.forget(); return preload.forget();
} }

View file

@ -2418,7 +2418,7 @@ nsresult imgLoader::LoadImage(
// preload image interacts with list of available images, see // preload image interacts with list of available images, see
// https://github.com/whatwg/html/issues/4474. // https://github.com/whatwg/html/issues/4474.
proxy->RemoveSelf(aLoadingDocument); proxy->RemoveSelf(aLoadingDocument);
proxy->NotifyUsage(); proxy->NotifyUsage(aLoadingDocument);
imgRequest* request = proxy->GetOwner(); imgRequest* request = proxy->GetOwner();
nsresult rv = nsresult rv =

View file

@ -1712,7 +1712,7 @@ void Loader::MaybeNotifyPreloadUsed(SheetLoadData& aData) {
return; return;
} }
preload->NotifyUsage(); preload->NotifyUsage(mDocument);
} }
Result<Loader::LoadSheetResult, nsresult> Loader::LoadInlineStyle( Result<Loader::LoadSheetResult, nsresult> Loader::LoadInlineStyle(

View file

@ -261,6 +261,7 @@ IPCResult DocumentChannelChild::RecvRedirectToRealChannel(
if (RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(newChannel)) { if (RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(newChannel)) {
httpChannel->SetEarlyHints(std::move(aArgs.earlyHints())); httpChannel->SetEarlyHints(std::move(aArgs.earlyHints()));
httpChannel->SetEarlyHintLinkType(aArgs.earlyHintLinkType());
} }
// This is used to report any errors back to the parent by calling // This is used to report any errors back to the parent by calling

View file

@ -97,7 +97,8 @@ bool DocumentChannelParent::Init(dom::CanonicalBrowsingContext* aContext,
auto promise = self->RedirectToRealChannel( auto promise = self->RedirectToRealChannel(
std::move(aResolveValue.mStreamFilterEndpoints), std::move(aResolveValue.mStreamFilterEndpoints),
aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags, aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags,
std::move(aResolveValue.mEarlyHints)); std::move(aResolveValue.mEarlyHints),
aResolveValue.mEarlyHintLinkType);
// We chain the promise the DLL is waiting on to the one returned by // We chain the promise the DLL is waiting on to the one returned by
// RedirectToRealChannel. As soon as the promise returned is resolved // RedirectToRealChannel. As soon as the promise returned is resolved
// or rejected, so will the DLL's promise. // or rejected, so will the DLL's promise.
@ -137,7 +138,7 @@ DocumentChannelParent::RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&& nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints, aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags, uint32_t aRedirectFlags, uint32_t aLoadFlags,
nsTArray<EarlyHintConnectArgs>&& aEarlyHints) { nsTArray<EarlyHintConnectArgs>&& aEarlyHints, uint32_t aEarlyHintLinkType) {
if (!CanSend()) { if (!CanSend()) {
return PDocumentChannelParent::RedirectToRealChannelPromise:: return PDocumentChannelParent::RedirectToRealChannelPromise::
CreateAndReject(ResponseRejectReason::ChannelClosed, __func__); CreateAndReject(ResponseRejectReason::ChannelClosed, __func__);
@ -145,8 +146,8 @@ DocumentChannelParent::RedirectToRealChannel(
RedirectToRealChannelArgs args; RedirectToRealChannelArgs args;
mDocumentLoadListener->SerializeRedirectData( mDocumentLoadListener->SerializeRedirectData(
args, false, aRedirectFlags, aLoadFlags, args, false, aRedirectFlags, aLoadFlags,
static_cast<ContentParent*>(Manager()->Manager()), static_cast<ContentParent*>(Manager()->Manager()), std::move(aEarlyHints),
std::move(aEarlyHints)); aEarlyHintLinkType);
return SendRedirectToRealChannel(args, std::move(aStreamFilterEndpoints)); return SendRedirectToRealChannel(args, std::move(aStreamFilterEndpoints));
} }

View file

@ -55,7 +55,8 @@ class DocumentChannelParent final
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&& nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints, aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags, uint32_t aRedirectFlags, uint32_t aLoadFlags,
nsTArray<EarlyHintConnectArgs>&& aEarlyHints); nsTArray<EarlyHintConnectArgs>&& aEarlyHints,
uint32_t aEarlyHintLinkType);
virtual ~DocumentChannelParent(); virtual ~DocumentChannelParent();

View file

@ -1457,10 +1457,12 @@ bool DocumentLoadListener::ResumeSuspendedChannel(
void DocumentLoadListener::SerializeRedirectData( void DocumentLoadListener::SerializeRedirectData(
RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess, RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess,
uint32_t aRedirectFlags, uint32_t aLoadFlags, ContentParent* aParent, uint32_t aRedirectFlags, uint32_t aLoadFlags, ContentParent* aParent,
nsTArray<EarlyHintConnectArgs>&& aEarlyHints) const { nsTArray<EarlyHintConnectArgs>&& aEarlyHints,
uint32_t aEarlyHintLinkType) const {
aArgs.uri() = GetChannelCreationURI(); aArgs.uri() = GetChannelCreationURI();
aArgs.loadIdentifier() = mLoadIdentifier; aArgs.loadIdentifier() = mLoadIdentifier;
aArgs.earlyHints() = std::move(aEarlyHints); aArgs.earlyHints() = std::move(aEarlyHints);
aArgs.earlyHintLinkType() = aEarlyHintLinkType;
// I previously used HttpBaseChannel::CloneLoadInfoForRedirect, but that // I previously used HttpBaseChannel::CloneLoadInfoForRedirect, but that
// clears the principal to inherit, which fails tests (probably because this // clears the principal to inherit, which fails tests (probably because this
@ -2096,7 +2098,8 @@ DocumentLoadListener::RedirectToRealChannel(
RedirectToRealChannelArgs args; RedirectToRealChannelArgs args;
SerializeRedirectData(args, /* aIsCrossProcess */ true, aRedirectFlags, SerializeRedirectData(args, /* aIsCrossProcess */ true, aRedirectFlags,
aLoadFlags, cp, std::move(ehArgs)); aLoadFlags, cp, std::move(ehArgs),
mEarlyHintsService.LinkType());
if (mTiming) { if (mTiming) {
mTiming->Anonymize(args.uri()); mTiming->Anonymize(args.uri());
args.timing() = std::move(mTiming); args.timing() = std::move(mTiming);
@ -2142,10 +2145,11 @@ DocumentLoadListener::RedirectToRealChannel(
nsTArray<EarlyHintConnectArgs> ehArgs; nsTArray<EarlyHintConnectArgs> ehArgs;
mEarlyHintsService.RegisterLinksAndGetConnectArgs(ehArgs); mEarlyHintsService.RegisterLinksAndGetConnectArgs(ehArgs);
mOpenPromise->Resolve(OpenPromiseSucceededType( mOpenPromise->Resolve(
{std::move(aStreamFilterEndpoints), aRedirectFlags, OpenPromiseSucceededType({std::move(aStreamFilterEndpoints),
aLoadFlags, std::move(ehArgs), promise}), aRedirectFlags, aLoadFlags, std::move(ehArgs),
__func__); mEarlyHintsService.LinkType(), promise}),
__func__);
// There is no way we could come back here if the promise had been resolved // There is no way we could come back here if the promise had been resolved
// previously. But for clarity and to avoid all doubt, we set this boolean to // previously. But for clarity and to avoid all doubt, we set this boolean to

View file

@ -111,6 +111,7 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
uint32_t mRedirectFlags; uint32_t mRedirectFlags;
uint32_t mLoadFlags; uint32_t mLoadFlags;
nsTArray<EarlyHintConnectArgs> mEarlyHints; nsTArray<EarlyHintConnectArgs> mEarlyHints;
uint32_t mEarlyHintLinkType;
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private> RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>
mPromise; mPromise;
}; };
@ -279,10 +280,11 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// Serializes all data needed to setup the new replacement channel // Serializes all data needed to setup the new replacement channel
// in the content process into the RedirectToRealChannelArgs struct. // in the content process into the RedirectToRealChannelArgs struct.
void SerializeRedirectData( void SerializeRedirectData(RedirectToRealChannelArgs& aArgs,
RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess, bool aIsCrossProcess, uint32_t aRedirectFlags,
uint32_t aRedirectFlags, uint32_t aLoadFlags, dom::ContentParent* aParent, uint32_t aLoadFlags, dom::ContentParent* aParent,
nsTArray<EarlyHintConnectArgs>&& aEarlyHints) const; nsTArray<EarlyHintConnectArgs>&& aEarlyHints,
uint32_t aEarlyHintLinkType) const;
uint64_t GetLoadIdentifier() const { return mLoadIdentifier; } uint64_t GetLoadIdentifier() const { return mLoadIdentifier; }
uint32_t GetLoadType() const { return mLoadStateLoadType; } uint32_t GetLoadType() const { return mLoadStateLoadType; }

View file

@ -493,6 +493,7 @@ struct RedirectToRealChannelArgs {
uint64_t loadIdentifier; uint64_t loadIdentifier;
nsCString? originalUriString; nsCString? originalUriString;
EarlyHintConnectArgs[] earlyHints; EarlyHintConnectArgs[] earlyHints;
uint32_t earlyHintLinkType;
}; };
struct TimingStructArgs { struct TimingStructArgs {

View file

@ -8,6 +8,7 @@
#include "EarlyHintsService.h" #include "EarlyHintsService.h"
#include "EarlyHintPreconnect.h" #include "EarlyHintPreconnect.h"
#include "EarlyHintPreloader.h" #include "EarlyHintPreloader.h"
#include "mozilla/dom/LinkStyle.h"
#include "mozilla/PreloadHashKey.h" #include "mozilla/PreloadHashKey.h"
#include "mozilla/Telemetry.h" #include "mozilla/Telemetry.h"
#include "mozilla/glean/GleanMetrics.h" #include "mozilla/glean/GleanMetrics.h"
@ -88,8 +89,10 @@ void EarlyHintsService::EarlyHint(const nsACString& aLinkHeader,
for (auto& linkHeader : linkHeaders) { for (auto& linkHeader : linkHeaders) {
CollectLinkTypeTelemetry(linkHeader.mRel); CollectLinkTypeTelemetry(linkHeader.mRel);
if (linkHeader.mRel.LowerCaseEqualsLiteral("preconnect")) { if (linkHeader.mRel.LowerCaseEqualsLiteral("preconnect")) {
mLinkType |= dom::LinkStyle::ePRECONNECT;
EarlyHintPreconnect::MaybePreconnect(linkHeader, aBaseURI, principal); EarlyHintPreconnect::MaybePreconnect(linkHeader, aBaseURI, principal);
} else if (linkHeader.mRel.LowerCaseEqualsLiteral("preload")) { } else if (linkHeader.mRel.LowerCaseEqualsLiteral("preload")) {
mLinkType |= dom::LinkStyle::ePRELOAD;
EarlyHintPreloader::MaybeCreateAndInsertPreload( EarlyHintPreloader::MaybeCreateAndInsertPreload(
mOngoingEarlyHints, linkHeader, aBaseURI, principal, mOngoingEarlyHints, linkHeader, aBaseURI, principal,
cookieJarSettings, aReferrerPolicy, aCSPHeader); cookieJarSettings, aReferrerPolicy, aCSPHeader);

View file

@ -35,6 +35,8 @@ class EarlyHintsService {
void RegisterLinksAndGetConnectArgs( void RegisterLinksAndGetConnectArgs(
nsTArray<EarlyHintConnectArgs>& aOutLinks); nsTArray<EarlyHintConnectArgs>& aOutLinks);
uint32_t LinkType() const { return mLinkType; }
private: private:
void CollectTelemetry(Maybe<uint32_t> aResponseStatus); void CollectTelemetry(Maybe<uint32_t> aResponseStatus);
void CollectLinkTypeTelemetry(const nsAString& aRel); void CollectLinkTypeTelemetry(const nsAString& aRel);
@ -43,6 +45,7 @@ class EarlyHintsService {
uint32_t mEarlyHintsCount{0}; uint32_t mEarlyHintsCount{0};
RefPtr<OngoingEarlyHints> mOngoingEarlyHints; RefPtr<OngoingEarlyHints> mOngoingEarlyHints;
uint32_t mLinkType = 0;
}; };
} // namespace mozilla::net } // namespace mozilla::net

View file

@ -6129,5 +6129,18 @@ void HttpBaseChannel::LogORBError(const nsAString& aReason) {
nsContentUtils::eNECKO_PROPERTIES, nsContentUtils::eNECKO_PROPERTIES,
"ResourceBlockedORB", params); "ResourceBlockedORB", params);
} }
NS_IMETHODIMP HttpBaseChannel::SetEarlyHintLinkType(
uint32_t aEarlyHintLinkType) {
mEarlyHintLinkType = aEarlyHintLinkType;
return NS_OK;
}
NS_IMETHODIMP HttpBaseChannel::GetEarlyHintLinkType(
uint32_t* aEarlyHintLinkType) {
*aEarlyHintLinkType = mEarlyHintLinkType;
return NS_OK;
}
} // namespace net } // namespace net
} // namespace mozilla } // namespace mozilla

View file

@ -352,6 +352,9 @@ class HttpBaseChannel : public nsHashPropertyBag,
NS_IMETHOD SetEarlyHintPreloaderId(uint64_t aEarlyHintPreloaderId) override; NS_IMETHOD SetEarlyHintPreloaderId(uint64_t aEarlyHintPreloaderId) override;
NS_IMETHOD GetEarlyHintPreloaderId(uint64_t* aEarlyHintPreloaderId) override; NS_IMETHOD GetEarlyHintPreloaderId(uint64_t* aEarlyHintPreloaderId) override;
NS_IMETHOD SetEarlyHintLinkType(uint32_t aEarlyHintLinkType) override;
NS_IMETHOD GetEarlyHintLinkType(uint32_t* aEarlyHintLinkType) override;
NS_IMETHOD SetClassicScriptHintCharset( NS_IMETHOD SetClassicScriptHintCharset(
const nsAString& aClassicScriptHintCharset) override; const nsAString& aClassicScriptHintCharset) override;
NS_IMETHOD GetClassicScriptHintCharset( NS_IMETHOD GetClassicScriptHintCharset(
@ -822,6 +825,7 @@ class HttpBaseChannel : public nsHashPropertyBag,
// EarlyHintRegistrar id to connect back to the preload. Set on preload // EarlyHintRegistrar id to connect back to the preload. Set on preload
// channels started from the above list // channels started from the above list
uint64_t mEarlyHintPreloaderId = 0; uint64_t mEarlyHintPreloaderId = 0;
uint32_t mEarlyHintLinkType = 0;
nsString mClassicScriptHintCharset; nsString mClassicScriptHintCharset;
nsString mDocumentCharacterSet; nsString mDocumentCharacterSet;

View file

@ -506,4 +506,10 @@ interface nsIHttpChannelInternal : nsISupports
*/ */
[must_use] void setWebTransportSessionEventListener( [must_use] void setWebTransportSessionEventListener(
in WebTransportSessionEventListener aListener); in WebTransportSessionEventListener aListener);
/**
* This attribute indicates the type of Link header in the received
* 103 response.
*/
[must_use] attribute unsigned long earlyHintLinkType;
}; };

View file

@ -17311,6 +17311,32 @@
"kind": "enumerated", "kind": "enumerated",
"description": "The usage of a early hint preload request" "description": "The usage of a early hint preload request"
}, },
"EH_PERF_PAGE_LOAD_TIME_MS": {
"record_in_processes": ["content"],
"products": ["firefox"],
"alert_emails": ["necko@mozilla.com", "kershaw@mozilla.com"],
"expires_in_version": "never",
"releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 50000,
"n_buckets": 100,
"bug_numbers": [1808323],
"keyed": true,
"description": "Time in milliseconds from navigationStart to loadEventStart for the foreground http or https root content document. This is collected only on page load where the main document uses or suppports Early Hints preload."
},
"EH_PERF_FIRST_CONTENTFUL_PAINT_MS": {
"record_in_processes": ["content"],
"products": ["firefox"],
"alert_emails": ["necko@mozilla.com", "kershaw@mozilla.com"],
"expires_in_version": "never",
"releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 50000,
"n_buckets": 100,
"bug_numbers": [1808323],
"keyed": true,
"description": "The time between navigationStart and the first contentful paint of a foreground http or https root content document, in milliseconds. The contentful paint timestamp is taken during display list building and does not include rasterization or compositing of that paint. This is collected only on page load where the main document uses or supports Early Hints preload."
},
"FX_PICTURE_IN_PICTURE_WINDOW_OPEN_DURATION": { "FX_PICTURE_IN_PICTURE_WINDOW_OPEN_DURATION": {
"record_in_processes": ["main"], "record_in_processes": ["main"],
"products": ["firefox"], "products": ["firefox"],

View file

@ -188,7 +188,12 @@ PreloadService::PreloadOrCoalesceResult PreloadService::PreloadOrCoalesce(
PreloadFetch(uri, aCORS, aReferrerPolicy, aEarlyHintPreloaderId); PreloadFetch(uri, aCORS, aReferrerPolicy, aEarlyHintPreloaderId);
} }
return {LookupPreload(preloadKey), false}; RefPtr<PreloaderBase> preload = LookupPreload(preloadKey);
if (preload && aEarlyHintPreloaderId) {
preload->SetForEarlyHints();
}
return {preload, false};
} }
void PreloadService::PreloadScript(nsIURI* aURI, const nsAString& aType, void PreloadService::PreloadScript(nsIURI* aURI, const nsAString& aType,

View file

@ -98,6 +98,9 @@ class PreloadService {
static void NotifyNodeEvent(nsINode* aNode, bool aSuccess); static void NotifyNodeEvent(nsINode* aNode, bool aSuccess);
void SetEarlyHintUsed() { mEarlyHintUsed = true; }
bool GetEarlyHintUsed() const { return mEarlyHintUsed; }
private: private:
dom::ReferrerPolicy PreloadReferrerPolicy(const nsAString& aReferrerPolicy); dom::ReferrerPolicy PreloadReferrerPolicy(const nsAString& aReferrerPolicy);
nsIURI* BaseURIForPreload(); nsIURI* BaseURIForPreload();
@ -123,6 +126,8 @@ class PreloadService {
// Set by `nsHtml5TreeOpExecutor::SetSpeculationBase`. // Set by `nsHtml5TreeOpExecutor::SetSpeculationBase`.
nsCOMPtr<nsIURI> mSpeculationBaseURI; nsCOMPtr<nsIURI> mSpeculationBaseURI;
bool mEarlyHintUsed = false;
}; };
} // namespace mozilla } // namespace mozilla

View file

@ -157,7 +157,8 @@ void PreloaderBase::NotifyOpen(const PreloadHashKey& aKey, nsIChannel* aChannel,
mChannel->SetNotificationCallbacks(sink); mChannel->SetNotificationCallbacks(sink);
} }
void PreloaderBase::NotifyUsage(LoadBackground aLoadBackground) { void PreloaderBase::NotifyUsage(dom::Document* aDocument,
LoadBackground aLoadBackground) {
if (!mIsUsed && mChannel && aLoadBackground == LoadBackground::Drop) { if (!mIsUsed && mChannel && aLoadBackground == LoadBackground::Drop) {
nsLoadFlags loadFlags; nsLoadFlags loadFlags;
mChannel->GetLoadFlags(&loadFlags); mChannel->GetLoadFlags(&loadFlags);
@ -186,6 +187,9 @@ void PreloaderBase::NotifyUsage(LoadBackground aLoadBackground) {
mIsUsed = true; mIsUsed = true;
ReportUsageTelemetry(); ReportUsageTelemetry();
CancelUsageTimer(); CancelUsageTimer();
if (mIsEarlyHintsPreload) {
aDocument->Preloads().SetEarlyHintUsed();
}
} }
void PreloaderBase::RemoveSelf(dom::Document* aDocument) { void PreloaderBase::RemoveSelf(dom::Document* aDocument) {

View file

@ -72,7 +72,8 @@ class PreloaderBase : public SupportsWeakPtr, public nsISupports {
// progress, will not be removed the LOAD_BACKGROUND flag, for instance XHR is // progress, will not be removed the LOAD_BACKGROUND flag, for instance XHR is
// the user here. // the user here.
enum class LoadBackground { Keep, Drop }; enum class LoadBackground { Keep, Drop };
void NotifyUsage(LoadBackground aLoadBackground = LoadBackground::Drop); void NotifyUsage(dom::Document* aDocument,
LoadBackground aLoadBackground = LoadBackground::Drop);
// Whether this preloader has been used for a regular/actual load or not. // Whether this preloader has been used for a regular/actual load or not.
bool IsUsed() const { return mIsUsed; } bool IsUsed() const { return mIsUsed; }
@ -122,6 +123,8 @@ class PreloaderBase : public SupportsWeakPtr, public nsISupports {
const nsTArray<RedirectRecord>& Redirects() { return mRedirectRecords; } const nsTArray<RedirectRecord>& Redirects() { return mRedirectRecords; }
void SetForEarlyHints() { mIsEarlyHintsPreload = true; }
protected: protected:
virtual ~PreloaderBase(); virtual ~PreloaderBase();
@ -183,6 +186,9 @@ class PreloaderBase : public SupportsWeakPtr, public nsISupports {
// True after we have reported the usage telemetry. Prevent duplicates. // True after we have reported the usage telemetry. Prevent duplicates.
bool mUsageTelementryReported = false; bool mUsageTelementryReported = false;
// True when this is used to Early Hints preload.
bool mIsEarlyHintsPreload = false;
// Emplaced when the data delivery has finished, in NotifyStop, holds the // Emplaced when the data delivery has finished, in NotifyStop, holds the
// result of the load. // result of the load.
Maybe<nsresult> mOnStopStatus; Maybe<nsresult> mOnStopStatus;