зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1753730) for causing bc failures on browser_103_preload.js.
Backed out changeset 8f27dc8dbbe0 (bug 1753730) Backed out changeset 3c25877d2660 (bug 1753730) Backed out changeset 47787105e3ad (bug 1753730)
This commit is contained in:
Родитель
660ff47e74
Коммит
f65b926595
|
@ -1264,10 +1264,6 @@ void CanonicalBrowsingContext::AddFinalDiscardListener(
|
|||
mFullyDiscardedListeners.AppendElement(std::move(aListener));
|
||||
}
|
||||
|
||||
net::EarlyHintsService* CanonicalBrowsingContext::GetEarlyHintsService() {
|
||||
return &mEarlyHintsService;
|
||||
}
|
||||
|
||||
void CanonicalBrowsingContext::AdjustPrivateBrowsingCount(
|
||||
bool aPrivateBrowsing) {
|
||||
if (IsDiscarded() || !EverAttached() || IsChrome()) {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#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"
|
||||
|
@ -362,8 +361,6 @@ 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();
|
||||
|
@ -567,8 +564,6 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
|||
bool mFullyDiscarded = false;
|
||||
|
||||
nsTArray<std::function<void(uint64_t)>> mFullyDiscardedListeners;
|
||||
|
||||
net::EarlyHintsService mEarlyHintsService;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -10338,12 +10338,6 @@
|
|||
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,9 +1104,7 @@ void DocumentLoadListener::Disconnect(bool aContinueNavigating) {
|
|||
httpChannelImpl->SetEarlyHintObserver(nullptr);
|
||||
}
|
||||
|
||||
if (GetLoadingBrowsingContext()) {
|
||||
GetLoadingBrowsingContext()->mEarlyHintsService.Cancel();
|
||||
}
|
||||
mEarlyHintsService.Cancel();
|
||||
|
||||
if (auto* ctx = GetDocumentBrowsingContext()) {
|
||||
ctx->EndDocumentLoad(aContinueNavigating);
|
||||
|
@ -2474,15 +2472,12 @@ DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
|
|||
}
|
||||
}
|
||||
|
||||
if (GetLoadingBrowsingContext()) {
|
||||
if (httpChannel) {
|
||||
uint32_t responseStatus;
|
||||
Unused << httpChannel->GetResponseStatus(&responseStatus);
|
||||
GetLoadingBrowsingContext()->mEarlyHintsService.FinalResponse(
|
||||
responseStatus);
|
||||
} else {
|
||||
GetLoadingBrowsingContext()->mEarlyHintsService.Cancel();
|
||||
}
|
||||
if (httpChannel) {
|
||||
uint32_t responseStatus;
|
||||
Unused << httpChannel->GetResponseStatus(&responseStatus);
|
||||
mEarlyHintsService.FinalResponse(responseStatus);
|
||||
} else {
|
||||
mEarlyHintsService.Cancel();
|
||||
}
|
||||
|
||||
// If we're going to be delivering this channel to a remote content
|
||||
|
@ -2853,11 +2848,8 @@ NS_IMETHODIMP DocumentLoadListener::OnStatus(nsIRequest* aRequest,
|
|||
|
||||
NS_IMETHODIMP DocumentLoadListener::EarlyHint(const nsACString& linkHeader) {
|
||||
LOG(("DocumentLoadListener::EarlyHint.\n"));
|
||||
if (GetLoadingBrowsingContext()) {
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
|
||||
GetLoadingBrowsingContext()->mEarlyHintsService.EarlyHint(
|
||||
linkHeader, GetChannelCreationURI(), loadInfo);
|
||||
}
|
||||
|
||||
mEarlyHintsService.EarlyHint(linkHeader);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -619,6 +619,8 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
|
|||
bool mOpenPromiseResolved = false;
|
||||
|
||||
const bool mIsDocumentLoad;
|
||||
|
||||
EarlyHintsService mEarlyHintsService;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(DocumentLoadListener, DOCUMENT_LOAD_LISTENER_IID)
|
||||
|
|
|
@ -1,314 +0,0 @@
|
|||
/* 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
|
|
@ -1,96 +0,0 @@
|
|||
/* 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,70 +6,26 @@
|
|||
* 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 {
|
||||
|
||||
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) {
|
||||
void EarlyHintsService::EarlyHint(const nsACString& linkHeader) {
|
||||
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,24 +8,17 @@
|
|||
#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();
|
||||
~EarlyHintsService();
|
||||
void EarlyHint(const nsACString& aLinkHeader, nsIURI* aBaseURI,
|
||||
nsILoadInfo* aLoadInfo);
|
||||
EarlyHintsService() = default;
|
||||
~EarlyHintsService() = default;
|
||||
void EarlyHint(const nsACString& linkHeader);
|
||||
void FinalResponse(uint32_t aResponseStatus);
|
||||
void Cancel();
|
||||
|
||||
|
@ -35,8 +28,6 @@ class EarlyHintsService {
|
|||
Maybe<TimeStamp> mFirstEarlyHint;
|
||||
uint32_t mEarlyHintsCount{0};
|
||||
bool mCanceled{false};
|
||||
|
||||
RefPtr<OngoingEarlyHints> mOngoingEarlyHints;
|
||||
};
|
||||
|
||||
} // namespace mozilla::net
|
||||
|
|
|
@ -50,7 +50,6 @@ EXPORTS.mozilla.net += [
|
|||
"ClassifierDummyChannelChild.h",
|
||||
"ClassifierDummyChannelParent.h",
|
||||
"ClassOfService.h",
|
||||
"EarlyHintPreloader.h",
|
||||
"EarlyHintsService.h",
|
||||
"HttpAuthUtils.h",
|
||||
"HttpBackgroundChannelChild.h",
|
||||
|
@ -102,7 +101,6 @@ UNIFIED_SOURCES += [
|
|||
"ConnectionEntry.cpp",
|
||||
"ConnectionHandle.cpp",
|
||||
"DnsAndConnectSocket.cpp",
|
||||
"EarlyHintPreloader.cpp",
|
||||
"EarlyHintsService.cpp",
|
||||
"Http2Compression.cpp",
|
||||
"Http2ConnectTransaction.cpp",
|
||||
|
|
|
@ -1976,7 +1976,7 @@ nsresult nsHttpTransaction::ParseLineSegment(char* segment, uint32_t len) {
|
|||
mResponseHead->Reset();
|
||||
return NS_OK;
|
||||
}
|
||||
if (!mConnection->IsProxyConnectInProgress()) {
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mEarlyHintObserver = nullptr;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<img src="http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs?138a85be-c7f1-4916-a6ef-c11c95d1cc4e" width="100px">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function checkNumberOfLoads(shouldBe) {
|
||||
var numDownloads = 0;
|
||||
window.performance.getEntriesByName("http://example.com/browser/netwerk/test/browser/square.png").forEach(entry => {
|
||||
if (entry.transferSize > 0) {
|
||||
numDownloads++;
|
||||
}
|
||||
});
|
||||
window.parent.ok(numDownloads == shouldBe, "The number of downloads of square.png is not correct");
|
||||
};
|
||||
|
||||
var img = document.createElement("img");
|
||||
img.src = "http://example.com/browser/netwerk/test/browser/square.png";
|
||||
img.addEventListener("load",() => {
|
||||
checkNumberOfLoads(1);
|
||||
window.parent.iframeTestCompleted();
|
||||
});
|
||||
|
||||
document.body.appendChild(img);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
HTTP 103 Too Early
|
||||
Link: <http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs?138a85be-c7f1-4916-a6ef-c11c95d1cc4e>; rel=preload; as=image
|
||||
Link: <http://example.com/browser/netwerk/test/browser/square.png>; rel=preload; as=image
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
HTTP 103 Early Hints
|
||||
Link: <http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs>; rel=preload; as=image
|
||||
Link: <http://example.com/browser/netwerk/test/browser/square.png>; rel=preload; as=image
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<img src="http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs?1ac2a5e1-90c7-4171-b0f0-676f7d899af3" width="100px">
|
||||
</body>
|
||||
</html>
|
|
@ -1,2 +0,0 @@
|
|||
Cache-Control: no-cache
|
||||
Content-Security-Policy: img-src 'none'
|
|
@ -1,2 +0,0 @@
|
|||
HTTP 103 Too Early
|
||||
Link: <http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs?1ac2a5e1-90c7-4171-b0f0-676f7d899af3>; rel=preload; as=image
|
|
@ -1,6 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<iframe src="/browser/netwerk/test/browser/early_hint_main_html.sjs?early_hint_pixel.sjs=5ecccd01-dd3f-4bbd-bd3e-0491d7dd78a1" width="100px">
|
||||
</body>
|
||||
</html>
|
|
@ -1 +0,0 @@
|
|||
Cache-Control: no-cache
|
|
@ -4,11 +4,6 @@ support-files =
|
|||
ioactivity.html
|
||||
redirect.sjs
|
||||
auth_post.sjs
|
||||
early_hint_main_html.sjs
|
||||
early_hint_pixel_count.sjs
|
||||
early_hint_redirect.sjs
|
||||
early_hint_pixel.sjs
|
||||
early_hint_error.sjs
|
||||
post.html
|
||||
res.css
|
||||
res.css^headers^
|
||||
|
@ -36,19 +31,15 @@ support-files =
|
|||
res_img_for_unknown_decoder^headers^
|
||||
res_object.html
|
||||
res_sub_document.html
|
||||
square.png
|
||||
103_preload.html
|
||||
103_preload.html^headers^
|
||||
103_preload.html^informationalResponse^
|
||||
103_preload.html^headers^
|
||||
no_103_preload.html
|
||||
no_103_preload.html^headers^
|
||||
103_preload_and_404.html^informationalResponse^
|
||||
103_preload_and_404.html^headers^
|
||||
103_preload_and_404.html
|
||||
103_preload_iframe.html
|
||||
103_preload_iframe.html^headers^
|
||||
103_preload_csp_imgsrc_none.html
|
||||
103_preload_csp_imgsrc_none.html^headers^
|
||||
103_preload_csp_imgsrc_none.html^informationalResponse^
|
||||
|
||||
[browser_about_cache.js]
|
||||
[browser_bug1535877.js]
|
||||
|
@ -78,6 +69,3 @@ support-files =
|
|||
[browser_103_telemetry.js]
|
||||
skip-if =
|
||||
os == 'linux' && bits == 64 && !debug # Bug 1744028 and Bug 1746324
|
||||
[browser_103_preload.js]
|
||||
skip-if =
|
||||
os == 'linux' && bits == 64 && !debug # Bug 1744028 and Bug 1746324
|
||||
|
|
|
@ -1,480 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const { TelemetryTestUtils } = ChromeUtils.import(
|
||||
"resource://testing-common/TelemetryTestUtils.jsm"
|
||||
);
|
||||
|
||||
Services.prefs.setCharPref(
|
||||
"dom.securecontext.allowlist",
|
||||
"example.com,example.net"
|
||||
);
|
||||
|
||||
Services.prefs.setBoolPref("network.early-hints.enabled", true);
|
||||
|
||||
async function test_hint_preload(
|
||||
testName,
|
||||
requestFrom,
|
||||
imgUrl,
|
||||
expectedRequestCount,
|
||||
uuid = undefined
|
||||
) {
|
||||
// generate a uuid if none were passed
|
||||
if (uuid == undefined) {
|
||||
uuid = Services.uuid.generateUUID();
|
||||
}
|
||||
await test_hint_preload_internal(
|
||||
testName,
|
||||
requestFrom,
|
||||
[[imgUrl, uuid.toString()]],
|
||||
expectedRequestCount
|
||||
);
|
||||
}
|
||||
|
||||
// - testName is just there to be printed during Asserts when failing
|
||||
// - the baseUrl can't have query strings, because they are currently used to pass
|
||||
// the early hint the server responds with
|
||||
// - urls are in the form [[url1, uuid1], ...]. The uuids are there to make each preload
|
||||
// unique and not available in the cache from other test cases
|
||||
// - expectedRequestCount is the sum of all requested objects { normal: count, hinted: count }
|
||||
async function test_hint_preload_internal(
|
||||
testName,
|
||||
requestFrom,
|
||||
imgUrls,
|
||||
expectedRequestCount
|
||||
) {
|
||||
// reset the count
|
||||
let headers = new Headers();
|
||||
headers.append("X-Early-Hint-Count-Start", "");
|
||||
await fetch(
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs",
|
||||
{ headers }
|
||||
);
|
||||
|
||||
// TODO: consider using http headers instead of query strings to pass the early hints
|
||||
// to allow the baseUrl
|
||||
let requestUrl =
|
||||
requestFrom +
|
||||
"/browser/netwerk/test/browser/early_hint_main_html.sjs?" +
|
||||
new URLSearchParams(imgUrls).toString(); // encode the hinted images as query string
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: requestUrl,
|
||||
waitForLoad: true,
|
||||
},
|
||||
async function() {}
|
||||
);
|
||||
|
||||
let gotRequestCount = await fetch(
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs"
|
||||
).then(response => response.json());
|
||||
|
||||
await Assert.deepEqual(
|
||||
gotRequestCount,
|
||||
expectedRequestCount,
|
||||
testName + ": Unexpected amount of requests made"
|
||||
);
|
||||
}
|
||||
|
||||
// TODO testing:
|
||||
// * Abort main document load while early hint is still loading -> early hint should be aborted
|
||||
|
||||
// two early hint responses
|
||||
add_task(async function test_103_two_preload_header() {
|
||||
await test_hint_preload_internal(
|
||||
"103_two_preload_header",
|
||||
"http://example.com",
|
||||
[
|
||||
[
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
Services.uuid.generateUUID().toString(),
|
||||
],
|
||||
["", "new_response"], // empty string to indicate new early hint response
|
||||
[
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
Services.uuid.generateUUID().toString(),
|
||||
],
|
||||
],
|
||||
{ hinted: 2, normal: 0 }
|
||||
);
|
||||
});
|
||||
|
||||
// two link header in one early hint response
|
||||
add_task(async function test_103_two_preload_header() {
|
||||
await test_hint_preload_internal(
|
||||
"103_two_preload_header",
|
||||
"http://example.com",
|
||||
[
|
||||
[
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
Services.uuid.generateUUID().toString(),
|
||||
],
|
||||
["", ""], // empty string to indicate new early hint response
|
||||
[
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
Services.uuid.generateUUID().toString(),
|
||||
],
|
||||
],
|
||||
{ hinted: 2, normal: 0 }
|
||||
);
|
||||
});
|
||||
|
||||
// two links in one early hint link header
|
||||
add_task(async function test_103_two_preload_header() {
|
||||
await test_hint_preload_internal(
|
||||
"103_two_preload_header",
|
||||
"http://example.com",
|
||||
[
|
||||
[
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
Services.uuid.generateUUID().toString(),
|
||||
],
|
||||
[
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
Services.uuid.generateUUID().toString(),
|
||||
],
|
||||
],
|
||||
{ hinted: 2, normal: 0 }
|
||||
);
|
||||
});
|
||||
|
||||
// Preload twice same origin in secure context
|
||||
add_task(async function test_103_preload_twice() {
|
||||
// pass two times the same uuid so that on the second request, the response is
|
||||
// already in the cache
|
||||
let uuid = Services.uuid.generateUUID();
|
||||
await test_hint_preload(
|
||||
"test_103_preload",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 1, normal: 0 },
|
||||
uuid
|
||||
);
|
||||
await test_hint_preload(
|
||||
"test_103_preload",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 0, normal: 0 },
|
||||
uuid
|
||||
);
|
||||
});
|
||||
|
||||
// Test that with config option disabled, no early hint requests are made
|
||||
add_task(async function test_103_preload() {
|
||||
Services.prefs.setBoolPref("network.early-hints.enabled", false);
|
||||
await test_hint_preload(
|
||||
"test_103_preload",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 0, normal: 1 }
|
||||
);
|
||||
Services.prefs.setBoolPref("network.early-hints.enabled", true);
|
||||
});
|
||||
|
||||
// Preload with same origin in secure context with mochitest http proxy
|
||||
add_task(async function test_103_preload_https() {
|
||||
await test_hint_preload(
|
||||
"test_103_preload_https",
|
||||
"https://example.org",
|
||||
"/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 1, normal: 0 }
|
||||
);
|
||||
});
|
||||
|
||||
// Preload with same origin in secure context
|
||||
add_task(async function test_103_preload() {
|
||||
await test_hint_preload(
|
||||
"test_103_preload",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 1, normal: 0 }
|
||||
);
|
||||
});
|
||||
|
||||
// Cross origin preload in secure context
|
||||
add_task(async function test_103_preload_cor() {
|
||||
await test_hint_preload(
|
||||
"test_103_preload_cor",
|
||||
"http://example.com",
|
||||
"http://example.net/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 0, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// Cross origin preload in insecure context
|
||||
add_task(async function test_103_preload_insecure_cor() {
|
||||
await test_hint_preload(
|
||||
"test_103_preload_insecure_cor",
|
||||
"http://example.com",
|
||||
"http://mochi.test:8888/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 0, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// Same origin request with relative url
|
||||
add_task(async function test_103_relative_preload() {
|
||||
await test_hint_preload(
|
||||
"test_103_relative_preload",
|
||||
"http://example.com",
|
||||
"/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 1, normal: 0 }
|
||||
);
|
||||
});
|
||||
|
||||
// Early hint from insecure context
|
||||
add_task(async function test_103_insecure_preload() {
|
||||
await test_hint_preload(
|
||||
"test_103_insecure_preload",
|
||||
"http://mochi.test:8888",
|
||||
"/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 0, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// Early hint to redirect to same origin in secure context
|
||||
add_task(async function test_103_redirect_same_origin() {
|
||||
await test_hint_preload(
|
||||
"test_103_redirect_same_origin",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_redirect.sjs?http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 2, normal: 0 } // successful preload of redirect and resulting image
|
||||
);
|
||||
});
|
||||
|
||||
// Early hint to redirect to cross origin in secure context
|
||||
add_task(async function test_103_redirect_cross_origin() {
|
||||
await test_hint_preload(
|
||||
"test_103_redirect_cross_origin",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_redirect.sjs?http://example.net/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 1, normal: 1 } // successful load of redirect in preload, but image loaded via normal load
|
||||
);
|
||||
});
|
||||
|
||||
// Early hint to redirect to cross origin in insecure context
|
||||
add_task(async function test_103_redirect_insecure_cross_origin() {
|
||||
await test_hint_preload(
|
||||
"test_103_redirect_insecure_cross_origin",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_redirect.sjs?http://mochi.test:8888/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// Cross origin preload from secure context to insecure context on same domain
|
||||
add_task(async function test_103_preload_mixed_content() {
|
||||
await test_hint_preload(
|
||||
"test_103_preload_mixed_content",
|
||||
"https://example.org",
|
||||
"http://example.org/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 0, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// Cross origin preload from secure context to redirected insecure context on same domain
|
||||
add_task(async function test_103_preload_redirect_mixed_content() {
|
||||
await test_hint_preload(
|
||||
"test_103_preload_redirect_mixed_content",
|
||||
"https://example.org",
|
||||
"https://example.org/browser/netwerk/test/browser/early_hint_redirect.sjs?http://example.org/browser/netwerk/test/browser/early_hint_pixel.sjs",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// Relative url, correct file for requested uri
|
||||
add_task(async function test_103_preload_only_file() {
|
||||
await test_hint_preload(
|
||||
"test_103_preload_only_file",
|
||||
"http://example.com",
|
||||
"early_hint_pixel.sjs",
|
||||
{ hinted: 1, normal: 0 }
|
||||
);
|
||||
});
|
||||
|
||||
// csp header with "img-src: 'none'" only on main html response, don't show the image on the page
|
||||
add_task(async function test_preload_csp_imgsrc_none() {
|
||||
// reset the count
|
||||
let headers = new Headers();
|
||||
headers.append("X-Early-Hint-Count-Start", "");
|
||||
await fetch(
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs",
|
||||
{ headers }
|
||||
);
|
||||
|
||||
let requestUrl =
|
||||
"http://example.com/browser/netwerk/test/browser/103_preload_csp_imgsrc_none.html";
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: requestUrl,
|
||||
waitForLoad: true,
|
||||
},
|
||||
async function(browser) {
|
||||
let noImgLoaded = await SpecialPowers.spawn(browser, [], function() {
|
||||
let loadInfo = content.performance.getEntriesByName(
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs?1ac2a5e1-90c7-4171-b0f0-676f7d899af3"
|
||||
);
|
||||
return loadInfo.every(entry => entry.decodedBodySize === 0);
|
||||
});
|
||||
await Assert.ok(
|
||||
noImgLoaded,
|
||||
"test_preload_csp_imgsrc_none: Image dislpayed unexpectedly"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
let gotRequestCount = await fetch(
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs"
|
||||
).then(response => response.json());
|
||||
|
||||
await Assert.deepEqual(
|
||||
gotRequestCount,
|
||||
{ hinted: 1, normal: 0 },
|
||||
"test_preload_csp_imgsrc_none: Unexpected amount of requests made"
|
||||
);
|
||||
});
|
||||
|
||||
// 400 Bad Request
|
||||
add_task(async function test_103_error_400() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_400",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?400",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// 401 Unauthorized
|
||||
add_task(async function test_103_error_401() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_401",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?401",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// 403 Forbidden
|
||||
add_task(async function test_103_error_403() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_403",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?403",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// 404 Not Found
|
||||
add_task(async function test_103_error_404() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_404",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?404",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// 408 Request Timeout
|
||||
add_task(async function test_103_error_408() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_408",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?408",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// 410 Gone
|
||||
add_task(async function test_103_error_410() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_410",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?410",
|
||||
{ hinted: 1, normal: 0 }
|
||||
);
|
||||
});
|
||||
|
||||
// 429 Too Many Requests
|
||||
add_task(async function test_103_error_429() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_429",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?429",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// 500 Internal Server Error
|
||||
add_task(async function test_103_error_500() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_500",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?500",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// 502 Bad Gateway
|
||||
add_task(async function test_103_error_502() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_502",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?502",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// 503 Service Unavailable
|
||||
add_task(async function test_103_error_503() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_503",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?503",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// 504 Gateway Timeout
|
||||
add_task(async function test_103_error_504() {
|
||||
await test_hint_preload(
|
||||
"test_103_error_504",
|
||||
"http://example.com",
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_error.sjs?504",
|
||||
{ hinted: 1, normal: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
// Test that preloads in iframes don't get triggered
|
||||
add_task(async function test_103_iframe() {
|
||||
// reset the count
|
||||
let headers = new Headers();
|
||||
headers.append("X-Early-Hint-Count-Start", "");
|
||||
await fetch(
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs",
|
||||
{ headers }
|
||||
);
|
||||
|
||||
let iframeUri =
|
||||
"http://example.com/browser/netwerk/test/browser/103_preload_iframe.html";
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: iframeUri,
|
||||
waitForLoad: true,
|
||||
},
|
||||
async function() {}
|
||||
);
|
||||
|
||||
let gotRequestCount = await fetch(
|
||||
"http://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs"
|
||||
).then(response => response.json());
|
||||
|
||||
await Assert.deepEqual(
|
||||
gotRequestCount,
|
||||
{ hinted: 0, normal: 1 },
|
||||
"test_103_iframe: Unexpected amount of requests made"
|
||||
);
|
||||
});
|
|
@ -1,37 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
Cu.importGlobalProperties(["URLSearchParams"]);
|
||||
|
||||
function handleRequest(request, response) {
|
||||
response.setStatusLine(
|
||||
request.httpVersion,
|
||||
parseInt(request.queryString),
|
||||
"Dynamic error"
|
||||
);
|
||||
response.setHeader("Content-Type", "image/png", false);
|
||||
response.setHeader("Cache-Control", "max-age=604800", false);
|
||||
|
||||
// count requests
|
||||
let image;
|
||||
let count = JSON.parse(getSharedState("earlyHintCount"));
|
||||
if (
|
||||
request.hasHeader("X-Moz") &&
|
||||
request.getHeader("X-Moz") === "early hint"
|
||||
) {
|
||||
count.hinted += 1;
|
||||
// set to green/black horizontal stripes (71 bytes)
|
||||
image = atob(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAADklEQVQIW2OU+i/FAAcADoABNV8X" +
|
||||
"GBMAAAAASUVORK5CYII="
|
||||
);
|
||||
} else {
|
||||
count.normal += 1;
|
||||
// set to purple/white checkered pattern (76 bytes)
|
||||
image = atob(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAE0lEQVQIW2P4//+/N8MkBiAGsgA1" +
|
||||
"bAe1SzDY8gAAAABJRU5ErkJggg=="
|
||||
);
|
||||
}
|
||||
setSharedState("earlyHintCount", JSON.stringify(count));
|
||||
response.write(image);
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
function handleRequest(request, response) {
|
||||
Cu.importGlobalProperties(["URLSearchParams"]);
|
||||
|
||||
// write to raw socket
|
||||
response.seizePower();
|
||||
|
||||
let qs = new URLSearchParams(request.queryString);
|
||||
let imgs = [];
|
||||
let new_hint = true;
|
||||
let new_header = true;
|
||||
for (const [imgUrl, uuid] of qs.entries()) {
|
||||
if (new_hint) {
|
||||
// we need to write a new header
|
||||
new_hint = false;
|
||||
response.write("HTTP/1.1 103 Early Hint\r\n");
|
||||
}
|
||||
if (imgUrl.length == 0) {
|
||||
// next hint in new early hint response when empty string is passed
|
||||
new_header = true;
|
||||
if (uuid === "new_response") {
|
||||
new_hint = true;
|
||||
response.write("\r\n");
|
||||
}
|
||||
response.write("\r\n");
|
||||
} else {
|
||||
// either append link in new header or in same header
|
||||
if (new_header) {
|
||||
new_header = false;
|
||||
response.write("Link: ");
|
||||
} else {
|
||||
response.write(", ");
|
||||
}
|
||||
// add query string to make request unique this has the drawback that
|
||||
// the preloaded image can't accept query strings on it's own / or has
|
||||
// to strip the appended "?uuid" from the query string before parsing
|
||||
imgs.push(`<img src="${imgUrl}?${uuid}" width="100px">`);
|
||||
response.write(`<${imgUrl}?${uuid}>; rel=preload; as=image`);
|
||||
}
|
||||
}
|
||||
if (!new_hint) {
|
||||
// add separator to main document
|
||||
response.write("\r\n\r\n");
|
||||
}
|
||||
|
||||
let body = `<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
${imgs.join("\n")}
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
// main document response
|
||||
response.write("HTTP/1.1 200 OK\r\n");
|
||||
response.write("Content-Type: text/html;charset=utf-8\r\n");
|
||||
response.write("Cache-Control: no-cache\r\n");
|
||||
response.write(`Content-Length: ${body.length}\r\n`);
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
function handleRequest(request, response) {
|
||||
response.setHeader("Content-Type", "image/png", false);
|
||||
response.setHeader("Cache-Control", "max-age=604800", false);
|
||||
|
||||
let count = JSON.parse(getSharedState("earlyHintCount"));
|
||||
let image;
|
||||
// send different sized images depending whether this is an early hint request
|
||||
if (
|
||||
request.hasHeader("X-Moz") &&
|
||||
request.getHeader("X-Moz") === "early hint"
|
||||
) {
|
||||
count.hinted += 1;
|
||||
// set to green/black horizontal stripes (71 bytes)
|
||||
image = atob(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAADklEQVQIW2OU+i/FAAcADoABNV8X" +
|
||||
"GBMAAAAASUVORK5CYII="
|
||||
);
|
||||
} else {
|
||||
count.normal += 1;
|
||||
// set to purple/white checkered pattern (76 bytes)
|
||||
image = atob(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAE0lEQVQIW2P4//+/N8MkBiAGsgA1" +
|
||||
"bAe1SzDY8gAAAABJRU5ErkJggg=="
|
||||
);
|
||||
}
|
||||
setSharedState("earlyHintCount", JSON.stringify(count));
|
||||
response.write(image);
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
function handleRequest(request, response) {
|
||||
if (request.hasHeader("X-Early-Hint-Count-Start")) {
|
||||
setSharedState("earlyHintCount", JSON.stringify({ hinted: 0, normal: 0 }));
|
||||
}
|
||||
response.setHeader("Content-Type", "application/json", false);
|
||||
response.write(getSharedState("earlyHintCount"));
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
function handleRequest(request, response) {
|
||||
// increase count
|
||||
let count = JSON.parse(getSharedState("earlyHintCount"));
|
||||
if (
|
||||
request.hasHeader("X-Moz") &&
|
||||
request.getHeader("X-Moz") === "early hint"
|
||||
) {
|
||||
count.hinted += 1;
|
||||
} else {
|
||||
count.normal += 1;
|
||||
}
|
||||
setSharedState("earlyHintCount", JSON.stringify(count));
|
||||
|
||||
// respond with redirect
|
||||
response.setStatusLine(request.httpVersion, 301, "Moved Permanently");
|
||||
let location = request.queryString;
|
||||
response.setHeader("Location", location, false);
|
||||
response.write("Hello world!");
|
||||
}
|
|
@ -1,6 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<img src="http://example.com/browser/netwerk/test/browser/early_hint_pixel.sjs" width="100px">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function checkNumberOfLoads(shouldBe) {
|
||||
var numDownloads = 0;
|
||||
window.performance.getEntriesByName("http://example.com/browser/netwerk/test/browser/square.png").forEach(entry => {
|
||||
if (entry.transferSize > 0) {
|
||||
numDownloads++;
|
||||
}
|
||||
});
|
||||
window.parent.ok(numDownloads == shouldBe, "The number of downloads of square.png is not correct");
|
||||
};
|
||||
|
||||
var img = document.createElement("img");
|
||||
img.src = "http://example.com/browser/netwerk/test/browser/square.png";
|
||||
img.addEventListener("load",() => {
|
||||
checkNumberOfLoads(1);
|
||||
window.parent.iframeTestCompleted();
|
||||
});
|
||||
|
||||
document.body.appendChild(img);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Загрузка…
Ссылка в новой задаче