Bug 1771867 - Early Hints Phase 2 - Part 8: Add list of open channels from OngoingEarlyHints to RedirectToRealChannelArgs r=necko-reviewers,valentin,kershaw

Differential Revision: https://phabricator.services.mozilla.com/D161179
This commit is contained in:
Manuel Bucher 2022-12-02 16:15:56 +00:00
parent 17df5816aa
commit b7aff527da
12 changed files with 87 additions and 51 deletions

View File

@ -1297,10 +1297,6 @@ void CanonicalBrowsingContext::AddFinalDiscardListener(
mFullyDiscardedListeners.AppendElement(std::move(aListener));
}
net::EarlyHintsService* CanonicalBrowsingContext::GetEarlyHintsService() {
return &mEarlyHintsService;
}
void CanonicalBrowsingContext::AdjustPrivateBrowsingCount(
bool aPrivateBrowsing) {
if (IsDiscarded() || !EverAttached() || IsChrome()) {

View File

@ -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"
@ -363,8 +362,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();
@ -568,8 +565,6 @@ class CanonicalBrowsingContext final : public BrowsingContext {
bool mFullyDiscarded = false;
nsTArray<std::function<void(uint64_t)>> mFullyDiscardedListeners;
net::EarlyHintsService mEarlyHintsService;
};
} // namespace dom

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ClientInfo.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "nsDocShellLoadState.h"
extern mozilla::LazyLogModule gDocumentChannelLog;
@ -96,7 +97,8 @@ bool DocumentChannelParent::Init(dom::CanonicalBrowsingContext* aContext,
// PDocumentChannel::RedirectToRealChannelPromise given as parameter.
auto promise = self->RedirectToRealChannel(
std::move(aResolveValue.mStreamFilterEndpoints),
aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags);
aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags,
std::move(aResolveValue.mEarlyHints));
// We chain the promise the DLL is waiting on to the one returned by
// RedirectToRealChannel. As soon as the promise returned is resolved
// or rejected, so will the DLL's promise.
@ -135,7 +137,8 @@ RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
DocumentChannelParent::RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags) {
uint32_t aRedirectFlags, uint32_t aLoadFlags,
nsTArray<EarlyHintConnectArgs>&& aEarlyHints) {
if (!CanSend()) {
return PDocumentChannelParent::RedirectToRealChannelPromise::
CreateAndReject(ResponseRejectReason::ChannelClosed, __func__);
@ -143,7 +146,8 @@ DocumentChannelParent::RedirectToRealChannel(
RedirectToRealChannelArgs args;
mDocumentLoadListener->SerializeRedirectData(
args, false, aRedirectFlags, aLoadFlags,
static_cast<ContentParent*>(Manager()->Manager()));
static_cast<ContentParent*>(Manager()->Manager()),
std::move(aEarlyHints));
return SendRedirectToRealChannel(args, std::move(aStreamFilterEndpoints));
}

View File

@ -16,6 +16,8 @@ class CanonicalBrowsingContext;
}
namespace net {
class EarlyHintConnectArgs;
/**
* An actor that forwards all changes across to DocumentChannelChild, the
* nsIChannel implementation owned by a content process docshell.
@ -52,7 +54,8 @@ class DocumentChannelParent final
RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags);
uint32_t aRedirectFlags, uint32_t aLoadFlags,
nsTArray<EarlyHintConnectArgs>&& aEarlyHints);
virtual ~DocumentChannelParent();

View File

@ -1203,8 +1203,8 @@ void DocumentLoadListener::Disconnect(bool aContinueNavigating) {
// Don't cancel ongoing early hints when continuing to load the web page.
// Early hints are loaded earlier in the code and shouldn't get cancelled
// here. See also: Bug 1765652
if (GetLoadingBrowsingContext() && !aContinueNavigating) {
GetLoadingBrowsingContext()->mEarlyHintsService.Cancel();
if (!aContinueNavigating) {
mEarlyHintsService.Cancel();
}
if (auto* ctx = GetDocumentBrowsingContext()) {
@ -1486,10 +1486,11 @@ bool DocumentLoadListener::ResumeSuspendedChannel(
void DocumentLoadListener::SerializeRedirectData(
RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess,
uint32_t aRedirectFlags, uint32_t aLoadFlags,
ContentParent* aParent) const {
uint32_t aRedirectFlags, uint32_t aLoadFlags, ContentParent* aParent,
nsTArray<EarlyHintConnectArgs>&& aEarlyHints) const {
aArgs.uri() = GetChannelCreationURI();
aArgs.loadIdentifier() = mLoadIdentifier;
aArgs.earlyHints() = std::move(aEarlyHints);
// I previously used HttpBaseChannel::CloneLoadInfoForRedirect, but that
// clears the principal to inherit, which fails tests (probably because this
@ -2120,9 +2121,12 @@ DocumentLoadListener::RedirectToRealChannel(
CreateAndReject(ipc::ResponseRejectReason::SendError, __func__);
}
nsTArray<EarlyHintConnectArgs> ehArgs;
mEarlyHintsService.RegisterLinksAndGetConnectArgs(ehArgs);
RedirectToRealChannelArgs args;
SerializeRedirectData(args, /* aIsCrossProcess */ true, aRedirectFlags,
aLoadFlags, cp);
aLoadFlags, cp, std::move(ehArgs));
if (mTiming) {
mTiming->Anonymize(args.uri());
args.timing() = Some(std::move(mTiming));
@ -2158,10 +2162,14 @@ DocumentLoadListener::RedirectToRealChannel(
auto promise =
MakeRefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>(
__func__);
mOpenPromise->Resolve(
OpenPromiseSucceededType({std::move(aStreamFilterEndpoints),
aRedirectFlags, aLoadFlags, promise}),
__func__);
nsTArray<EarlyHintConnectArgs> ehArgs;
mEarlyHintsService.RegisterLinksAndGetConnectArgs(ehArgs);
mOpenPromise->Resolve(OpenPromiseSucceededType(
{std::move(aStreamFilterEndpoints), aRedirectFlags,
aLoadFlags, std::move(ehArgs), promise}),
__func__);
// 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
@ -2576,15 +2584,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 = 0;
Unused << httpChannel->GetResponseStatus(&responseStatus);
mEarlyHintsService.FinalResponse(responseStatus);
} else {
mEarlyHintsService.Cancel();
}
// If we're going to be delivering this channel to a remote content
@ -2804,19 +2809,23 @@ DocumentLoadListener::AsyncOnChannelRedirect(
return NS_OK;
}
// Cancel cross origin redirects as described by whatwg:
// > Note: [The early hint reponse] is discarded if it is succeeded by a
// > cross-origin redirect.
// https://html.spec.whatwg.org/multipage/semantics.html#early-hints
nsCOMPtr<nsIURI> oldURI;
aOldChannel->GetURI(getter_AddRefs(oldURI));
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsresult rv = ssm->CheckSameOriginURI(oldURI, uri, false, false);
if (NS_FAILED(rv)) {
mEarlyHintsService.Cancel();
}
if (GetDocumentBrowsingContext()) {
nsCOMPtr<nsIURI> oldURI;
aOldChannel->GetURI(getter_AddRefs(oldURI));
if (!net::ChannelIsPost(aOldChannel)) {
AddURIVisit(aOldChannel, 0);
nsDocShell::SaveLastVisit(aNewChannel, oldURI, aFlags);
}
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsresult rv = ssm->CheckSameOriginURI(oldURI, uri, false, false);
if (NS_FAILED(rv)) {
GetLoadingBrowsingContext()->mEarlyHintsService.Cancel();
}
}
mHaveVisibleRedirect |= true;
@ -2949,10 +2958,7 @@ NS_IMETHODIMP DocumentLoadListener::OnStatus(nsIRequest* aRequest,
NS_IMETHODIMP DocumentLoadListener::EarlyHint(const nsACString& linkHeader) {
LOG(("DocumentLoadListener::EarlyHint.\n"));
if (GetLoadingBrowsingContext()) {
GetLoadingBrowsingContext()->mEarlyHintsService.EarlyHint(
linkHeader, GetChannelCreationURI(), mChannel);
}
mEarlyHintsService.EarlyHint(linkHeader, GetChannelCreationURI(), mChannel);
return NS_OK;
}

View File

@ -110,6 +110,7 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
mStreamFilterEndpoints;
uint32_t mRedirectFlags;
uint32_t mLoadFlags;
nsTArray<EarlyHintConnectArgs> mEarlyHints;
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>
mPromise;
};
@ -278,10 +279,10 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// Serializes all data needed to setup the new replacement channel
// in the content process into the RedirectToRealChannelArgs struct.
void SerializeRedirectData(RedirectToRealChannelArgs& aArgs,
bool aIsCrossProcess, uint32_t aRedirectFlags,
uint32_t aLoadFlags,
dom::ContentParent* aParent) const;
void SerializeRedirectData(
RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess,
uint32_t aRedirectFlags, uint32_t aLoadFlags, dom::ContentParent* aParent,
nsTArray<EarlyHintConnectArgs>&& aEarlyHints) const;
uint64_t GetLoadIdentifier() const { return mLoadIdentifier; }
uint32_t GetLoadType() const { return mLoadStateLoadType; }
@ -513,6 +514,8 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// switch occurs.
RefPtr<nsDOMNavigationTiming> mTiming;
net::EarlyHintsService mEarlyHintsService;
// An optional ObjectUpgradeHandler which can be used to upgrade an <object>
// or <embed> element to contain a nsFrameLoader, allowing us to switch them
// into a different process.

View File

@ -48,7 +48,8 @@ RefPtr<RedirectToRealChannelPromise>
ParentProcessDocumentChannel::RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags) {
uint32_t aRedirectFlags, uint32_t aLoadFlags,
const nsTArray<EarlyHintConnectArgs>& aEarlyHints) {
LOG(("ParentProcessDocumentChannel RedirectToRealChannel [this=%p]", this));
nsCOMPtr<nsIChannel> channel = mDocumentLoadListener->GetChannel();
channel->SetLoadFlags(aLoadFlags);
@ -207,7 +208,8 @@ NS_IMETHODIMP ParentProcessDocumentChannel::AsyncOpen(
RefPtr<RedirectToRealChannelPromise> p =
self->RedirectToRealChannel(
std::move(aResolveValue.mStreamFilterEndpoints),
aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags)
aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags,
aResolveValue.mEarlyHints)
->Then(
GetCurrentSerialEventTarget(), __func__,
[self](RedirectToRealChannelPromise::ResolveOrRejectValue&&

View File

@ -16,6 +16,8 @@
namespace mozilla {
namespace net {
class EarlyHintConnectArgs;
class ParentProcessDocumentChannel : public DocumentChannel,
public nsIAsyncVerifyRedirectCallback,
public nsIObserver {
@ -38,7 +40,8 @@ class ParentProcessDocumentChannel : public DocumentChannel,
RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags);
uint32_t aRedirectFlags, uint32_t aLoadFlags,
const nsTArray<EarlyHintConnectArgs>& aEarlyHints);
private:
virtual ~ParentProcessDocumentChannel();

View File

@ -66,6 +66,11 @@ bool OngoingEarlyHints::Add(const PreloadHashKey& aKey,
return mOngoingPreloads.InsertOrUpdate(aKey, aPreloader);
}
void OngoingEarlyHints::RegisterLinksAndGetConnectArgs(
nsTArray<EarlyHintConnectArgs>& aOutLinks) {
// register all channels before returning
}
//=============================================================================
// EarlyHintPreloader
//=============================================================================

View File

@ -22,6 +22,7 @@ class nsIReferrerInfo;
namespace mozilla::net {
class EarlyHintPreloader;
class EarlyHintConnectArgs;
struct LinkHeader;
// class keeping track of all ongoing early hints
@ -38,9 +39,17 @@ class OngoingEarlyHints final {
void CancelAllOngoingPreloads();
// registers all channels and returns the ids
void RegisterLinksAndGetConnectArgs(
nsTArray<EarlyHintConnectArgs>& aOutLinks);
private:
~OngoingEarlyHints() = default;
nsRefPtrHashtable<PreloadHashKey, EarlyHintPreloader> mOngoingPreloads;
// keep track of all preloads in the order they were specified in the early
// hint header
nsTArray<EarlyHintConnectArgs> mLinks;
};
class EarlyHintPreloader final : public nsIStreamListener,

View File

@ -100,6 +100,11 @@ void EarlyHintsService::Cancel() {
}
}
void EarlyHintsService::RegisterLinksAndGetConnectArgs(
nsTArray<EarlyHintConnectArgs>& aOutLinks) {
mOngoingEarlyHints->RegisterLinksAndGetConnectArgs(aOutLinks);
}
void EarlyHintsService::CollectTelemetry(Maybe<uint32_t> aResponseStatus) {
// EH_NUM_OF_HINTS_PER_PAGE is only collected for the 2xx responses,
// regardless of the number of received mEarlyHintsCount.

View File

@ -9,15 +9,17 @@
#define mozilla_net_EarlyHintsService_h
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "nsStringFwd.h"
#include "mozilla/RefPtr.h"
#include "nsTArray.h"
class nsIChannel;
class nsIURI;
namespace mozilla::net {
class EarlyHintConnectArgs;
class OngoingEarlyHints;
class EarlyHintsService {
@ -29,6 +31,9 @@ class EarlyHintsService {
void FinalResponse(uint32_t aResponseStatus);
void Cancel();
void RegisterLinksAndGetConnectArgs(
nsTArray<EarlyHintConnectArgs>& aOutLinks);
private:
void CollectTelemetry(Maybe<uint32_t> aResponseStatus);
void CollectLinkTypeTelemetry(const nsAString& aRel);