зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1753730 - Add EarlyHintPreloader to load 103 Early Hint responses into the cache r=necko-reviewers,ckerschb,dragana,kershaw
Currently only same origin requests are preloaded and preloads in the secure context. This may change in the future to match W3C decisions and Chromes behavior. Also only images get preloaded. This will change in the future to cover asset types. Currently the anchor isn't parsed correctly yet[1], so this will be fixed in a future patch. On non-2xx responses of the main document all ongoing preloads get canceled. Already completed preloads don't get affected and are in the cache. It is currently untested whether unused preloads don't have side effects. Another future patch should cover adding the preload to the devtools. [1]: https://datatracker.ietf.org/doc/html/rfc8288#section-3.2 Differential Revision: https://phabricator.services.mozilla.com/D137885
This commit is contained in:
Родитель
d2e465a0b2
Коммит
2952cf8ce7
|
@ -1264,6 +1264,10 @@ void CanonicalBrowsingContext::AddFinalDiscardListener(
|
|||
mFullyDiscardedListeners.AppendElement(std::move(aListener));
|
||||
}
|
||||
|
||||
net::EarlyHintsService* CanonicalBrowsingContext::GetEarlyHintsService() {
|
||||
return &mEarlyHintsService;
|
||||
}
|
||||
|
||||
void CanonicalBrowsingContext::AdjustPrivateBrowsingCount(
|
||||
bool aPrivateBrowsing) {
|
||||
if (IsDiscarded() || !EverAttached() || IsChrome()) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef mozilla_dom_CanonicalBrowsingContext_h
|
||||
#define mozilla_dom_CanonicalBrowsingContext_h
|
||||
|
||||
#include "mozilla/net/EarlyHintsService.h"
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "mozilla/dom/MediaControlKeySource.h"
|
||||
#include "mozilla/dom/BrowsingContextWebProgress.h"
|
||||
|
@ -361,6 +362,8 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
|||
|
||||
void AddFinalDiscardListener(std::function<void(uint64_t)>&& aListener);
|
||||
|
||||
net::EarlyHintsService* GetEarlyHintsService();
|
||||
|
||||
protected:
|
||||
// Called when the browsing context is being discarded.
|
||||
void CanonicalDiscard();
|
||||
|
@ -564,6 +567,8 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
|||
bool mFullyDiscarded = false;
|
||||
|
||||
nsTArray<std::function<void(uint64_t)>> mFullyDiscardedListeners;
|
||||
|
||||
net::EarlyHintsService mEarlyHintsService;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -10338,6 +10338,12 @@
|
|||
value: true
|
||||
mirror: always
|
||||
|
||||
# Enable 103 Early Hint status code (RFC 8297)
|
||||
- name: network.early-hints.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# Whether to use the network process or not
|
||||
# Start a separate socket process. Performing networking on the socket process
|
||||
# is control by a sepparate pref
|
||||
|
|
|
@ -1104,7 +1104,9 @@ void DocumentLoadListener::Disconnect(bool aContinueNavigating) {
|
|||
httpChannelImpl->SetEarlyHintObserver(nullptr);
|
||||
}
|
||||
|
||||
mEarlyHintsService.Cancel();
|
||||
if (GetLoadingBrowsingContext()) {
|
||||
GetLoadingBrowsingContext()->mEarlyHintsService.Cancel();
|
||||
}
|
||||
|
||||
if (auto* ctx = GetDocumentBrowsingContext()) {
|
||||
ctx->EndDocumentLoad(aContinueNavigating);
|
||||
|
@ -2472,12 +2474,15 @@ DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
|
|||
}
|
||||
}
|
||||
|
||||
if (httpChannel) {
|
||||
uint32_t responseStatus;
|
||||
Unused << httpChannel->GetResponseStatus(&responseStatus);
|
||||
mEarlyHintsService.FinalResponse(responseStatus);
|
||||
} else {
|
||||
mEarlyHintsService.Cancel();
|
||||
if (GetLoadingBrowsingContext()) {
|
||||
if (httpChannel) {
|
||||
uint32_t responseStatus;
|
||||
Unused << httpChannel->GetResponseStatus(&responseStatus);
|
||||
GetLoadingBrowsingContext()->mEarlyHintsService.FinalResponse(
|
||||
responseStatus);
|
||||
} else {
|
||||
GetLoadingBrowsingContext()->mEarlyHintsService.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// If we're going to be delivering this channel to a remote content
|
||||
|
@ -2848,8 +2853,11 @@ NS_IMETHODIMP DocumentLoadListener::OnStatus(nsIRequest* aRequest,
|
|||
|
||||
NS_IMETHODIMP DocumentLoadListener::EarlyHint(const nsACString& linkHeader) {
|
||||
LOG(("DocumentLoadListener::EarlyHint.\n"));
|
||||
|
||||
mEarlyHintsService.EarlyHint(linkHeader);
|
||||
if (GetLoadingBrowsingContext()) {
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
|
||||
GetLoadingBrowsingContext()->mEarlyHintsService.EarlyHint(
|
||||
linkHeader, GetChannelCreationURI(), loadInfo);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -619,8 +619,6 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
|
|||
bool mOpenPromiseResolved = false;
|
||||
|
||||
const bool mIsDocumentLoad;
|
||||
|
||||
EarlyHintsService mEarlyHintsService;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(DocumentLoadListener, DOCUMENT_LOAD_LISTENER_IID)
|
||||
|
|
|
@ -0,0 +1,314 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EarlyHintPreloader.h"
|
||||
|
||||
#include "EarlyHintsService.h"
|
||||
#include "ErrorList.h"
|
||||
#include "mozilla/CORSMode.h"
|
||||
#include "mozilla/dom/ReferrerInfo.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsAttrValue.h"
|
||||
#include "nsAttrValueInlines.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsICacheInfoChannel.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIReferrerInfo.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsStreamUtils.h"
|
||||
|
||||
//
|
||||
// To enable logging (see mozilla/Logging.h for full details):
|
||||
//
|
||||
// set MOZ_LOG=EarlyHint:5
|
||||
// set MOZ_LOG_FILE=earlyhint.log
|
||||
//
|
||||
// this enables LogLevel::Debug level information and places all output in
|
||||
// the file earlyhint.log
|
||||
//
|
||||
static mozilla::LazyLogModule gEarlyHintLog("EarlyHint");
|
||||
|
||||
#undef LOG
|
||||
#define LOG(args) MOZ_LOG(gEarlyHintLog, mozilla::LogLevel::Debug, args)
|
||||
|
||||
#undef LOG_ENABLED
|
||||
#define LOG_ENABLED() MOZ_LOG_TEST(gEarlyHintLog, mozilla::LogLevel::Debug)
|
||||
|
||||
namespace mozilla::net {
|
||||
|
||||
//=============================================================================
|
||||
// OngoingEarlyHints
|
||||
//=============================================================================
|
||||
|
||||
void OngoingEarlyHints::CancelAllOngoingPreloads() {
|
||||
for (auto& el : mOngoingPreloads) {
|
||||
el.GetData()->CancelChannel(nsresult::NS_ERROR_ABORT);
|
||||
}
|
||||
}
|
||||
|
||||
bool OngoingEarlyHints::Contains(const PreloadHashKey& aKey) {
|
||||
return mOngoingPreloads.Contains(aKey);
|
||||
}
|
||||
|
||||
bool OngoingEarlyHints::Add(const PreloadHashKey& aKey,
|
||||
RefPtr<EarlyHintPreloader> aPreloader) {
|
||||
return mOngoingPreloads.InsertOrUpdate(aKey, aPreloader);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// EarlyHintPreloader
|
||||
//=============================================================================
|
||||
|
||||
EarlyHintPreloader::EarlyHintPreloader(nsIURI* aURI) : mURI(aURI) {}
|
||||
|
||||
/* static */
|
||||
Maybe<PreloadHashKey> EarlyHintPreloader::GenerateHashKey(
|
||||
ASDestination aAs, nsIURI* aURI, nsIPrincipal* aPrincipal) {
|
||||
if (aAs == ASDestination::DESTINATION_IMAGE) {
|
||||
return Some(
|
||||
PreloadHashKey::CreateAsImage(aURI, aPrincipal, CORSMode::CORS_NONE));
|
||||
}
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
// static
|
||||
void EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||
OngoingEarlyHints* aOngoingEarlyHints, const LinkHeader& aHeader,
|
||||
nsIURI* aBaseURI, nsIPrincipal* aTriggeringPrincipal,
|
||||
nsICookieJarSettings* aCookieJarSettings) {
|
||||
if (!aHeader.mRel.LowerCaseEqualsASCII("preload")) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAttrValue as;
|
||||
ParseAsValue(aHeader.mAs, as);
|
||||
if (as.GetEnumValue() == ASDestination::DESTINATION_INVALID) {
|
||||
// return early when it's definitly not an asset type we preload
|
||||
// would be caught later as well, e.g. when creating the PreloadHashKey
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
// use the base uri
|
||||
NS_ENSURE_SUCCESS_VOID(
|
||||
NS_NewURI(getter_AddRefs(uri), aHeader.mHref, nullptr, aBaseURI));
|
||||
|
||||
// Only make same origin preloads, the fromPrivateWindow is only read when
|
||||
// reportError is enabled, so setting both to false is safe.
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
|
||||
aBaseURI, uri, /* reportError */ false,
|
||||
/* fromPrivateWindow */ false))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<PreloadHashKey> hashKey = GenerateHashKey(
|
||||
static_cast<ASDestination>(as.GetEnumValue()), uri, aTriggeringPrincipal);
|
||||
if (!hashKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aOngoingEarlyHints->Contains(*hashKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsContentPolicyType contentPolicyType = AsValueToContentPolicy(as);
|
||||
if (contentPolicyType == nsContentPolicyType::TYPE_INVALID) {
|
||||
return;
|
||||
}
|
||||
|
||||
dom::ReferrerPolicy referrerPolicy =
|
||||
dom::ReferrerInfo::ReferrerPolicyAttributeFromString(
|
||||
aHeader.mReferrerPolicy);
|
||||
|
||||
nsCOMPtr<nsIReferrerInfo> referrerInfo =
|
||||
new dom::ReferrerInfo(aBaseURI, referrerPolicy);
|
||||
|
||||
RefPtr<EarlyHintPreloader> earlyHintPreloader =
|
||||
RefPtr(new EarlyHintPreloader(uri));
|
||||
|
||||
nsSecurityFlags securityFlags =
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT;
|
||||
|
||||
NS_ENSURE_SUCCESS_VOID(earlyHintPreloader->OpenChannel(
|
||||
aTriggeringPrincipal, securityFlags, contentPolicyType, referrerInfo,
|
||||
aCookieJarSettings));
|
||||
|
||||
DebugOnly<bool> result =
|
||||
aOngoingEarlyHints->Add(*hashKey, earlyHintPreloader);
|
||||
MOZ_ASSERT(result);
|
||||
}
|
||||
|
||||
nsresult EarlyHintPreloader::OpenChannel(
|
||||
nsIPrincipal* aTriggeringPrincipal, nsSecurityFlags aSecurityFlags,
|
||||
nsContentPolicyType aContentPolicyType, nsIReferrerInfo* aReferrerInfo,
|
||||
nsICookieJarSettings* aCookieJarSettings) {
|
||||
MOZ_ASSERT(aContentPolicyType == nsContentPolicyType::TYPE_IMAGE);
|
||||
nsresult rv =
|
||||
NS_NewChannel(getter_AddRefs(mChannel), mURI, aTriggeringPrincipal,
|
||||
aSecurityFlags, aContentPolicyType, aCookieJarSettings,
|
||||
/* aPerformanceStorage */ nullptr,
|
||||
/* aLoadGroup */ nullptr,
|
||||
/* aCallbacks */ this, nsIRequest::LOAD_NORMAL);
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// configure HTTP specific stuff
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
|
||||
if (!httpChannel) {
|
||||
mChannel = nullptr;
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
DebugOnly<nsresult> success = httpChannel->SetReferrerInfo(aReferrerInfo);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(success));
|
||||
success = httpChannel->SetRequestHeader("X-Moz"_ns, "early hint"_ns, false);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(success));
|
||||
|
||||
return mChannel->AsyncOpen(this);
|
||||
}
|
||||
|
||||
nsresult EarlyHintPreloader::CancelChannel(nsresult aStatus) {
|
||||
// clear redirect channel in case this channel is cleared between the call of
|
||||
// EarlyHintPreloader::AsyncOnChannelRedirect and
|
||||
// EarlyHintPreloader::OnRedirectResult
|
||||
mRedirectChannel = nullptr;
|
||||
if (mChannel) {
|
||||
mChannel->Cancel(aStatus);
|
||||
mChannel = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// EarlyHintPreloader::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS(EarlyHintPreloader, nsIRequestObserver, nsIStreamListener,
|
||||
nsIChannelEventSink, nsIInterfaceRequestor,
|
||||
nsIRedirectResultListener)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// EarlyHintPreloader::nsIStreamListener
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
EarlyHintPreloader::OnStartRequest(nsIRequest* aRequest) {
|
||||
LOG(("EarlyHintPreloader::OnStartRequest\n"));
|
||||
|
||||
nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel = do_QueryInterface(aRequest);
|
||||
if (!cacheInfoChannel) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
// no need to prefetch an asset that is already in the cache
|
||||
bool fromCache;
|
||||
if (NS_SUCCEEDED(cacheInfoChannel->IsFromCache(&fromCache)) && fromCache) {
|
||||
LOG(("document is already in the cache; canceling prefetch\n"));
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EarlyHintPreloader::OnDataAvailable(nsIRequest* aRequest,
|
||||
nsIInputStream* aStream, uint64_t aOffset,
|
||||
uint32_t aCount) {
|
||||
uint32_t bytesRead = 0;
|
||||
nsresult rv =
|
||||
aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &bytesRead);
|
||||
LOG(("prefetched %u bytes [offset=%" PRIu64 "]\n", bytesRead, aOffset));
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EarlyHintPreloader::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
|
||||
LOG(("EarlyHintPreloader::OnStopRequest\n"));
|
||||
mChannel = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// EarlyHintPreloader::nsIChannelEventSink
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
EarlyHintPreloader::AsyncOnChannelRedirect(
|
||||
nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
|
||||
nsIAsyncVerifyRedirectCallback* callback) {
|
||||
nsCOMPtr<nsIURI> newURI;
|
||||
nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aNewChannel->GetURI(getter_AddRefs(newURI));
|
||||
if (NS_FAILED(rv)) {
|
||||
callback->OnRedirectVerifyCallback(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// abort the request if redirecting to cross origin resource, the
|
||||
// fromPrivateWindow is only read when reportError is enabled, so setting both
|
||||
// to false is safe.
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
|
||||
mURI, newURI, /* reportError */ false,
|
||||
/* fromPrivateWindow */ false))) {
|
||||
callback->OnRedirectVerifyCallback(NS_ERROR_ABORT);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// HTTP request headers are not automatically forwarded to the new channel.
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
|
||||
NS_ENSURE_STATE(httpChannel);
|
||||
|
||||
rv = httpChannel->SetRequestHeader("X-Moz"_ns, "early hint"_ns, false);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
// Assign to mChannel after we get notification about success of the
|
||||
// redirect in OnRedirectResult.
|
||||
mRedirectChannel = aNewChannel;
|
||||
|
||||
callback->OnRedirectVerifyCallback(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// EarlyHintPreloader::nsIRedirectResultListener
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
EarlyHintPreloader::OnRedirectResult(bool aProceeding) {
|
||||
if (aProceeding && mRedirectChannel) {
|
||||
mChannel = mRedirectChannel;
|
||||
}
|
||||
|
||||
mRedirectChannel = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// EarlyHintPreloader::nsIInterfaceRequestor
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
EarlyHintPreloader::GetInterface(const nsIID& aIID, void** aResult) {
|
||||
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
|
||||
NS_ADDREF_THIS();
|
||||
*aResult = static_cast<nsIChannelEventSink*>(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) {
|
||||
NS_ADDREF_THIS();
|
||||
*aResult = static_cast<nsIRedirectResultListener*>(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
} // namespace mozilla::net
|
|
@ -0,0 +1,96 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_net_EarlyHintPreloader_h
|
||||
#define mozilla_net_EarlyHintPreloader_h
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/PreloadHashKey.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIRedirectResultListener.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
|
||||
class nsAttrValue;
|
||||
class nsICookieJarSettings;
|
||||
class nsIPrincipal;
|
||||
class nsIReferrerInfo;
|
||||
|
||||
namespace mozilla::net {
|
||||
|
||||
class EarlyHintPreloader;
|
||||
struct LinkHeader;
|
||||
|
||||
// class keeping track of all ongoing early hints
|
||||
class OngoingEarlyHints final {
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(OngoingEarlyHints)
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(OngoingEarlyHints)
|
||||
|
||||
OngoingEarlyHints() = default;
|
||||
|
||||
// returns whether a preload with that key already existed
|
||||
bool Contains(const PreloadHashKey& aKey);
|
||||
bool Add(const PreloadHashKey& aKey, RefPtr<EarlyHintPreloader> aPreloader);
|
||||
|
||||
void CancelAllOngoingPreloads();
|
||||
|
||||
private:
|
||||
~OngoingEarlyHints() = default;
|
||||
nsRefPtrHashtable<PreloadHashKey, EarlyHintPreloader> mOngoingPreloads;
|
||||
};
|
||||
|
||||
class EarlyHintPreloader final : public nsIStreamListener,
|
||||
public nsIChannelEventSink,
|
||||
public nsIRedirectResultListener,
|
||||
public nsIInterfaceRequestor {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
NS_DECL_NSIREDIRECTRESULTLISTENER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
public:
|
||||
// Create and insert a preload into OngoingEarlyHints if the same preload
|
||||
// wasn't already issued and the LinkHeader can be parsed correctly.
|
||||
static void MaybeCreateAndInsertPreload(
|
||||
OngoingEarlyHints* aOngoingEarlyHints, const LinkHeader& aHeader,
|
||||
nsIURI* aBaseURI, nsIPrincipal* aTriggeringPrincipal,
|
||||
nsICookieJarSettings* aCookieJarSettings);
|
||||
|
||||
// Should be called by the preloader service when the preload is not needed
|
||||
// after all, because the final response returns a non-2xx status code.
|
||||
nsresult CancelChannel(nsresult aStatus);
|
||||
|
||||
private:
|
||||
explicit EarlyHintPreloader(nsIURI* aURI);
|
||||
~EarlyHintPreloader() = default;
|
||||
|
||||
static Maybe<PreloadHashKey> GenerateHashKey(ASDestination aAs, nsIURI* aURI,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
// call to start the preload
|
||||
nsresult OpenChannel(nsIPrincipal* aTriggeringPrincipal,
|
||||
nsSecurityFlags aSecurityFlags,
|
||||
nsContentPolicyType aContentPolicyType,
|
||||
nsIReferrerInfo* aReferrerInfo,
|
||||
nsICookieJarSettings* aCookieJarSettings);
|
||||
|
||||
// keep opening uri to not preload cross origins on redirects for now
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsCOMPtr<nsIChannel> mRedirectChannel;
|
||||
};
|
||||
|
||||
inline nsISupports* ToSupports(EarlyHintPreloader* aObj) {
|
||||
return static_cast<nsIInterfaceRequestor*>(aObj);
|
||||
}
|
||||
|
||||
} // namespace mozilla::net
|
||||
|
||||
#endif // mozilla_net_EarlyHintPreloader_h
|
|
@ -6,26 +6,70 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EarlyHintsService.h"
|
||||
#include "EarlyHintPreloader.h"
|
||||
#include "mozilla/PreloadHashKey.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsICookieJarSettings.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/StaticPrefs_network.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsILoadInfo.h"
|
||||
|
||||
namespace mozilla::net {
|
||||
|
||||
void EarlyHintsService::EarlyHint(const nsACString& linkHeader) {
|
||||
EarlyHintsService::EarlyHintsService()
|
||||
: mOngoingEarlyHints(new OngoingEarlyHints()) {}
|
||||
|
||||
// implementing the destructor in the .cpp file to allow EarlyHintsService.h
|
||||
// not to include EarlyHintPreloader.h, decoupling the two files and hopefully
|
||||
// allow faster compile times
|
||||
EarlyHintsService::~EarlyHintsService() = default;
|
||||
|
||||
void EarlyHintsService::EarlyHint(const nsACString& aLinkHeader,
|
||||
nsIURI* aBaseURI, nsILoadInfo* aLoadInfo) {
|
||||
mEarlyHintsCount++;
|
||||
if (!mFirstEarlyHint) {
|
||||
mFirstEarlyHint.emplace(TimeStamp::NowLoRes());
|
||||
}
|
||||
|
||||
if (!StaticPrefs::network_early_hints_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> triggeringPrincipal = aLoadInfo->TriggeringPrincipal();
|
||||
|
||||
nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
|
||||
if (NS_FAILED(
|
||||
aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: find out why LinkHeaderParser uses utf16 and check if it can be
|
||||
// changed to utf8
|
||||
auto linkHeaders = ParseLinkHeader(NS_ConvertUTF8toUTF16(aLinkHeader));
|
||||
|
||||
for (auto& linkHeader : linkHeaders) {
|
||||
EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||
mOngoingEarlyHints, linkHeader, aBaseURI, triggeringPrincipal,
|
||||
cookieJarSettings);
|
||||
}
|
||||
}
|
||||
|
||||
void EarlyHintsService::FinalResponse(uint32_t aResponseStatus) {
|
||||
// We will collect telemetry mosly once for a document.
|
||||
// In case of a reddirect this will be called multiple times.
|
||||
CollectTelemetry(Some(aResponseStatus));
|
||||
if (aResponseStatus >= 300) {
|
||||
mOngoingEarlyHints->CancelAllOngoingPreloads();
|
||||
mCanceled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void EarlyHintsService::Cancel() {
|
||||
if (!mCanceled) {
|
||||
CollectTelemetry(Nothing());
|
||||
mOngoingEarlyHints->CancelAllOngoingPreloads();
|
||||
mCanceled = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,17 +8,24 @@
|
|||
#ifndef mozilla_net_EarlyHintsService_h
|
||||
#define mozilla_net_EarlyHintsService_h
|
||||
|
||||
#include "nsStringFwd.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsStringFwd.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
class nsILoadInfo;
|
||||
class nsIURI;
|
||||
|
||||
namespace mozilla::net {
|
||||
|
||||
class OngoingEarlyHints;
|
||||
|
||||
class EarlyHintsService {
|
||||
public:
|
||||
EarlyHintsService() = default;
|
||||
~EarlyHintsService() = default;
|
||||
void EarlyHint(const nsACString& linkHeader);
|
||||
EarlyHintsService();
|
||||
~EarlyHintsService();
|
||||
void EarlyHint(const nsACString& aLinkHeader, nsIURI* aBaseURI,
|
||||
nsILoadInfo* aLoadInfo);
|
||||
void FinalResponse(uint32_t aResponseStatus);
|
||||
void Cancel();
|
||||
|
||||
|
@ -28,6 +35,8 @@ class EarlyHintsService {
|
|||
Maybe<TimeStamp> mFirstEarlyHint;
|
||||
uint32_t mEarlyHintsCount{0};
|
||||
bool mCanceled{false};
|
||||
|
||||
RefPtr<OngoingEarlyHints> mOngoingEarlyHints;
|
||||
};
|
||||
|
||||
} // namespace mozilla::net
|
||||
|
|
|
@ -50,6 +50,7 @@ EXPORTS.mozilla.net += [
|
|||
"ClassifierDummyChannelChild.h",
|
||||
"ClassifierDummyChannelParent.h",
|
||||
"ClassOfService.h",
|
||||
"EarlyHintPreloader.h",
|
||||
"EarlyHintsService.h",
|
||||
"HttpAuthUtils.h",
|
||||
"HttpBackgroundChannelChild.h",
|
||||
|
@ -101,6 +102,7 @@ UNIFIED_SOURCES += [
|
|||
"ConnectionEntry.cpp",
|
||||
"ConnectionHandle.cpp",
|
||||
"DnsAndConnectSocket.cpp",
|
||||
"EarlyHintPreloader.cpp",
|
||||
"EarlyHintsService.cpp",
|
||||
"Http2Compression.cpp",
|
||||
"Http2ConnectTransaction.cpp",
|
||||
|
|
Загрузка…
Ссылка в новой задаче