forked from mirrors/gecko-dev
Bug 1798319 - Implement modulepreload in early hints r=manuel,smaug,necko-reviewers,kershaw
The aEarlyHintPreloaderId parameter for StartLoad/StartLoadInternal is changed to be a member variable of ScriptLoadRequest instead so that an initiator type of early hints can be set for module requests. Before, ModuleLoader would always pass in a zero value for the id since ModuleLoaderBase has no concept of early hints when it calls StartFetch. As a prerequisite for early hints support, this commit also implements modulepreload in link headers (Bug 1773056). Differential Revision: https://phabricator.services.mozilla.com/D180020
This commit is contained in:
parent
c92d9850e8
commit
b6b6fe577b
19 changed files with 204 additions and 63 deletions
|
|
@ -14,6 +14,7 @@
|
||||||
#include "mozilla/PresShell.h"
|
#include "mozilla/PresShell.h"
|
||||||
#include "mozilla/StaticPrefs_browser.h"
|
#include "mozilla/StaticPrefs_browser.h"
|
||||||
#include "mozilla/StaticPrefs_content.h"
|
#include "mozilla/StaticPrefs_content.h"
|
||||||
|
#include "mozilla/StaticPrefs_network.h"
|
||||||
#include "mozilla/dom/Document.h"
|
#include "mozilla/dom/Document.h"
|
||||||
#include "mozilla/dom/LinkStyle.h"
|
#include "mozilla/dom/LinkStyle.h"
|
||||||
#include "mozilla/dom/ReferrerInfo.h"
|
#include "mozilla/dom/ReferrerInfo.h"
|
||||||
|
|
@ -57,6 +58,14 @@
|
||||||
#include "nsSandboxFlags.h"
|
#include "nsSandboxFlags.h"
|
||||||
#include "Link.h"
|
#include "Link.h"
|
||||||
#include "HTMLLinkElement.h"
|
#include "HTMLLinkElement.h"
|
||||||
|
#include "MediaList.h"
|
||||||
|
#include "nsString.h"
|
||||||
|
#include "nsStringFwd.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "mozilla/RefPtr.h"
|
||||||
|
#include "nsCOMPtr.h"
|
||||||
|
#include "nsLiteralString.h"
|
||||||
|
#include "nsIContentPolicy.h"
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::css;
|
using namespace mozilla::css;
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
|
@ -309,9 +318,9 @@ nsresult nsContentSink::ProcessLinkFromHeader(const net::LinkHeader& aHeader,
|
||||||
|
|
||||||
if ((linkTypes & LinkStyle::eMODULE_PRELOAD) &&
|
if ((linkTypes & LinkStyle::eMODULE_PRELOAD) &&
|
||||||
mDocument->ScriptLoader()->GetModuleLoader()) {
|
mDocument->ScriptLoader()->GetModuleLoader()) {
|
||||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-modulepreload-module-script-graph
|
PreloadModule(aHeader.mHref, aHeader.mAs, aHeader.mMedia,
|
||||||
// Step 1. Disallow further import maps given settings object.
|
aHeader.mIntegrity, aHeader.mCrossOrigin,
|
||||||
mDocument->ScriptLoader()->GetModuleLoader()->DisallowImportMaps();
|
aHeader.mReferrerPolicy, aEarlyHintPreloaderId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -444,6 +453,50 @@ void nsContentSink::PreloadHref(const nsAString& aHref, const nsAString& aAs,
|
||||||
aReferrerPolicy, aEarlyHintPreloaderId);
|
aReferrerPolicy, aEarlyHintPreloaderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nsContentSink::PreloadModule(const nsAString& aHref, const nsAString& aAs,
|
||||||
|
const nsAString& aMedia,
|
||||||
|
const nsAString& aIntegrity,
|
||||||
|
const nsAString& aCORS,
|
||||||
|
const nsAString& aReferrerPolicy,
|
||||||
|
uint64_t aEarlyHintPreloaderId) {
|
||||||
|
ModuleLoader* moduleLoader = mDocument->ScriptLoader()->GetModuleLoader();
|
||||||
|
|
||||||
|
if (!StaticPrefs::network_modulepreload()) {
|
||||||
|
// Keep behavior from https://phabricator.services.mozilla.com/D149371,
|
||||||
|
// prior to main implementation of modulepreload
|
||||||
|
moduleLoader->DisallowImportMaps();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<mozilla::dom::MediaList> mediaList =
|
||||||
|
mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(aMedia));
|
||||||
|
if (!mediaList->Matches(*mDocument)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aHref.IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!net::IsScriptLikeOrInvalid(aAs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto encoding = mDocument->GetDocumentCharacterSet();
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI());
|
||||||
|
if (!uri) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleLoader->DisallowImportMaps();
|
||||||
|
|
||||||
|
mDocument->Preloads().PreloadLinkHeader(
|
||||||
|
uri, aHref, nsIContentPolicy::TYPE_SCRIPT, u"script"_ns, u"module"_ns,
|
||||||
|
aIntegrity, u""_ns, u""_ns, aCORS, aReferrerPolicy,
|
||||||
|
aEarlyHintPreloaderId);
|
||||||
|
}
|
||||||
|
|
||||||
void nsContentSink::PrefetchDNS(const nsAString& aHref) {
|
void nsContentSink::PrefetchDNS(const nsAString& aHref) {
|
||||||
nsAutoString hostname;
|
nsAutoString hostname;
|
||||||
bool isHttps = false;
|
bool isHttps = false;
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,11 @@ class nsContentSink : public nsICSSLoaderObserver,
|
||||||
const nsAString& aReferrerPolicy,
|
const nsAString& aReferrerPolicy,
|
||||||
uint64_t aEarlyHintPreloaderId);
|
uint64_t aEarlyHintPreloaderId);
|
||||||
|
|
||||||
|
void PreloadModule(const nsAString& aHref, const nsAString& aAs,
|
||||||
|
const nsAString& aMedia, const nsAString& aIntegrity,
|
||||||
|
const nsAString& aCORS, const nsAString& aReferrerPolicy,
|
||||||
|
uint64_t aEarlyHintPreloaderId);
|
||||||
|
|
||||||
// For PrefetchDNS() aHref can either be the usual
|
// For PrefetchDNS() aHref can either be the usual
|
||||||
// URI format or of the form "//www.hostname.com" without a scheme.
|
// URI format or of the form "//www.hostname.com" without a scheme.
|
||||||
void PrefetchDNS(const nsAString& aHref);
|
void PrefetchDNS(const nsAString& aHref);
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
#include "nsGlobalWindowInner.h"
|
#include "nsGlobalWindowInner.h"
|
||||||
#include "nsIPrincipal.h"
|
#include "nsIPrincipal.h"
|
||||||
#include "mozilla/LoadInfo.h"
|
#include "mozilla/LoadInfo.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
|
|
||||||
using JS::SourceText;
|
using JS::SourceText;
|
||||||
using namespace JS::loader;
|
using namespace JS::loader;
|
||||||
|
|
@ -112,7 +113,7 @@ nsresult ModuleLoader::StartFetch(ModuleLoadRequest* aRequest) {
|
||||||
// and `StartLoadInternal` is able to find the charset by using `aRequest`
|
// and `StartLoadInternal` is able to find the charset by using `aRequest`
|
||||||
// for this case.
|
// for this case.
|
||||||
nsresult rv = GetScriptLoader()->StartLoadInternal(
|
nsresult rv = GetScriptLoader()->StartLoadInternal(
|
||||||
aRequest, securityFlags, 0, Nothing() /* aCharsetForPreload */);
|
aRequest, securityFlags, Nothing() /* aCharsetForPreload */);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-an-import()-module-script-graph
|
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-an-import()-module-script-graph
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,8 @@
|
||||||
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
||||||
#include "nsIScriptError.h"
|
#include "nsIScriptError.h"
|
||||||
#include "nsIAsyncOutputStream.h"
|
#include "nsIAsyncOutputStream.h"
|
||||||
|
#include "js/loader/ModuleLoaderBase.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
|
|
||||||
using JS::SourceText;
|
using JS::SourceText;
|
||||||
using namespace JS::loader;
|
using namespace JS::loader;
|
||||||
|
|
@ -526,7 +528,7 @@ nsresult ScriptLoader::RestartLoad(ScriptLoadRequest* aRequest) {
|
||||||
if (aRequest->IsModuleRequest()) {
|
if (aRequest->IsModuleRequest()) {
|
||||||
rv = aRequest->AsModuleRequest()->RestartModuleLoad();
|
rv = aRequest->AsModuleRequest()->RestartModuleLoad();
|
||||||
} else {
|
} else {
|
||||||
rv = StartLoad(aRequest, 0, Nothing());
|
rv = StartLoad(aRequest, Nothing());
|
||||||
}
|
}
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return rv;
|
return rv;
|
||||||
|
|
@ -538,17 +540,17 @@ nsresult ScriptLoader::RestartLoad(ScriptLoadRequest* aRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult ScriptLoader::StartLoad(
|
nsresult ScriptLoader::StartLoad(
|
||||||
ScriptLoadRequest* aRequest, uint64_t aEarlyHintPreloaderId,
|
ScriptLoadRequest* aRequest,
|
||||||
const Maybe<nsAutoString>& aCharsetForPreload) {
|
const Maybe<nsAutoString>& aCharsetForPreload) {
|
||||||
if (aRequest->IsModuleRequest()) {
|
if (aRequest->IsModuleRequest()) {
|
||||||
return aRequest->AsModuleRequest()->StartModuleLoad();
|
return aRequest->AsModuleRequest()->StartModuleLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
return StartClassicLoad(aRequest, aEarlyHintPreloaderId, aCharsetForPreload);
|
return StartClassicLoad(aRequest, aCharsetForPreload);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult ScriptLoader::StartClassicLoad(
|
nsresult ScriptLoader::StartClassicLoad(
|
||||||
ScriptLoadRequest* aRequest, uint64_t aEarlyHintPreloaderId,
|
ScriptLoadRequest* aRequest,
|
||||||
const Maybe<nsAutoString>& aCharsetForPreload) {
|
const Maybe<nsAutoString>& aCharsetForPreload) {
|
||||||
MOZ_ASSERT(aRequest->IsFetching());
|
MOZ_ASSERT(aRequest->IsFetching());
|
||||||
NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
|
NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
|
||||||
|
|
@ -573,8 +575,7 @@ nsresult ScriptLoader::StartClassicLoad(
|
||||||
|
|
||||||
securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
|
securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
|
||||||
|
|
||||||
nsresult rv = StartLoadInternal(aRequest, securityFlags,
|
nsresult rv = StartLoadInternal(aRequest, securityFlags, aCharsetForPreload);
|
||||||
aEarlyHintPreloaderId, aCharsetForPreload);
|
|
||||||
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
|
@ -593,7 +594,6 @@ static bool IsWebExtensionRequest(ScriptLoadRequest* aRequest) {
|
||||||
|
|
||||||
nsresult ScriptLoader::StartLoadInternal(
|
nsresult ScriptLoader::StartLoadInternal(
|
||||||
ScriptLoadRequest* aRequest, nsSecurityFlags securityFlags,
|
ScriptLoadRequest* aRequest, nsSecurityFlags securityFlags,
|
||||||
uint64_t aEarlyHintPreloaderId,
|
|
||||||
const Maybe<nsAutoString>& aCharsetForPreload) {
|
const Maybe<nsAutoString>& aCharsetForPreload) {
|
||||||
nsContentPolicyType contentPolicyType =
|
nsContentPolicyType contentPolicyType =
|
||||||
ScriptLoadRequestToContentPolicyType(aRequest);
|
ScriptLoadRequestToContentPolicyType(aRequest);
|
||||||
|
|
@ -620,12 +620,13 @@ nsresult ScriptLoader::StartLoadInternal(
|
||||||
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (aEarlyHintPreloaderId) {
|
if (aRequest->mEarlyHintPreloaderId) {
|
||||||
nsCOMPtr<nsIHttpChannelInternal> channelInternal =
|
nsCOMPtr<nsIHttpChannelInternal> channelInternal =
|
||||||
do_QueryInterface(channel);
|
do_QueryInterface(channel);
|
||||||
NS_ENSURE_TRUE(channelInternal != nullptr, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(channelInternal != nullptr, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
rv = channelInternal->SetEarlyHintPreloaderId(aEarlyHintPreloaderId);
|
rv = channelInternal->SetEarlyHintPreloaderId(
|
||||||
|
aRequest->mEarlyHintPreloaderId);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -767,7 +768,7 @@ nsresult ScriptLoader::StartLoadInternal(
|
||||||
// Set the initiator type
|
// Set the initiator type
|
||||||
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
|
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
|
||||||
if (timedChannel) {
|
if (timedChannel) {
|
||||||
if (aEarlyHintPreloaderId) {
|
if (aRequest->mEarlyHintPreloaderId) {
|
||||||
timedChannel->SetInitiatorType(u"early-hints"_ns);
|
timedChannel->SetInitiatorType(u"early-hints"_ns);
|
||||||
} else if (aRequest->GetScriptLoadContext()->IsLinkPreloadScript()) {
|
} else if (aRequest->GetScriptLoadContext()->IsLinkPreloadScript()) {
|
||||||
timedChannel->SetInitiatorType(u"link"_ns);
|
timedChannel->SetInitiatorType(u"link"_ns);
|
||||||
|
|
@ -800,12 +801,13 @@ nsresult ScriptLoader::StartLoadInternal(
|
||||||
aRequest->GetScriptLoadContext()->IsLinkPreloadScript(),
|
aRequest->GetScriptLoadContext()->IsLinkPreloadScript(),
|
||||||
aRequest->IsModuleRequest());
|
aRequest->IsModuleRequest());
|
||||||
|
|
||||||
if (aEarlyHintPreloaderId) {
|
if (aRequest->mEarlyHintPreloaderId) {
|
||||||
nsCOMPtr<nsIHttpChannelInternal> channelInternal =
|
nsCOMPtr<nsIHttpChannelInternal> channelInternal =
|
||||||
do_QueryInterface(channel);
|
do_QueryInterface(channel);
|
||||||
NS_ENSURE_TRUE(channelInternal != nullptr, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(channelInternal != nullptr, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
rv = channelInternal->SetEarlyHintPreloaderId(aEarlyHintPreloaderId);
|
rv = channelInternal->SetEarlyHintPreloaderId(
|
||||||
|
aRequest->mEarlyHintPreloaderId);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
rv = channel->AsyncOpen(loader);
|
rv = channel->AsyncOpen(loader);
|
||||||
|
|
@ -1031,7 +1033,7 @@ bool ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
|
||||||
LOG(("ScriptLoadRequest (%p): Created request for external script",
|
LOG(("ScriptLoadRequest (%p): Created request for external script",
|
||||||
request.get()));
|
request.get()));
|
||||||
|
|
||||||
nsresult rv = StartLoad(request, 0, Nothing());
|
nsresult rv = StartLoad(request, Nothing());
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
ReportErrorToConsole(request, rv);
|
ReportErrorToConsole(request, rv);
|
||||||
|
|
||||||
|
|
@ -3568,6 +3570,7 @@ void ScriptLoader::PreloadURI(nsIURI* aURI, const nsAString& aCharset,
|
||||||
request->GetScriptLoadContext()->mScriptFromHead = aScriptFromHead;
|
request->GetScriptLoadContext()->mScriptFromHead = aScriptFromHead;
|
||||||
request->GetScriptLoadContext()->SetScriptMode(aDefer, aAsync, aLinkPreload);
|
request->GetScriptLoadContext()->SetScriptMode(aDefer, aAsync, aLinkPreload);
|
||||||
request->GetScriptLoadContext()->SetIsPreloadRequest();
|
request->GetScriptLoadContext()->SetIsPreloadRequest();
|
||||||
|
request->mEarlyHintPreloaderId = aEarlyHintPreloaderId;
|
||||||
|
|
||||||
if (LOG_ENABLED()) {
|
if (LOG_ENABLED()) {
|
||||||
nsAutoCString url;
|
nsAutoCString url;
|
||||||
|
|
@ -3577,7 +3580,7 @@ void ScriptLoader::PreloadURI(nsIURI* aURI, const nsAString& aCharset,
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAutoString charset(aCharset);
|
nsAutoString charset(aCharset);
|
||||||
nsresult rv = StartLoad(request, aEarlyHintPreloaderId, Some(charset));
|
nsresult rv = StartLoad(request, Some(charset));
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -506,14 +506,12 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
||||||
* Start a load for aRequest's URI.
|
* Start a load for aRequest's URI.
|
||||||
*/
|
*/
|
||||||
nsresult StartLoad(ScriptLoadRequest* aRequest,
|
nsresult StartLoad(ScriptLoadRequest* aRequest,
|
||||||
uint64_t aEarlyHintPreloaderId,
|
|
||||||
const Maybe<nsAutoString>& aCharsetForPreload);
|
const Maybe<nsAutoString>& aCharsetForPreload);
|
||||||
/**
|
/**
|
||||||
* Start a load for a classic script URI.
|
* Start a load for a classic script URI.
|
||||||
* Sets up the necessary security flags before calling StartLoadInternal.
|
* Sets up the necessary security flags before calling StartLoadInternal.
|
||||||
*/
|
*/
|
||||||
nsresult StartClassicLoad(ScriptLoadRequest* aRequest,
|
nsresult StartClassicLoad(ScriptLoadRequest* aRequest,
|
||||||
uint64_t aEarlyHintPreloaderId,
|
|
||||||
const Maybe<nsAutoString>& aCharsetForPreload);
|
const Maybe<nsAutoString>& aCharsetForPreload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -525,7 +523,6 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
||||||
*/
|
*/
|
||||||
nsresult StartLoadInternal(ScriptLoadRequest* aRequest,
|
nsresult StartLoadInternal(ScriptLoadRequest* aRequest,
|
||||||
nsSecurityFlags securityFlags,
|
nsSecurityFlags securityFlags,
|
||||||
uint64_t aEarlyHintPreloaderId,
|
|
||||||
const Maybe<nsAutoString>& aCharsetForPreload);
|
const Maybe<nsAutoString>& aCharsetForPreload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,8 @@ ScriptLoadRequest::ScriptLoadRequest(ScriptKind aKind, nsIURI* aURI,
|
||||||
mScriptBytecode(),
|
mScriptBytecode(),
|
||||||
mBytecodeOffset(0),
|
mBytecodeOffset(0),
|
||||||
mURI(aURI),
|
mURI(aURI),
|
||||||
mLoadContext(aContext) {
|
mLoadContext(aContext),
|
||||||
|
mEarlyHintPreloaderId(0) {
|
||||||
MOZ_ASSERT(mFetchOptions);
|
MOZ_ASSERT(mFetchOptions);
|
||||||
if (mLoadContext) {
|
if (mLoadContext) {
|
||||||
mLoadContext->SetRequest(this);
|
mLoadContext->SetRequest(this);
|
||||||
|
|
|
||||||
|
|
@ -369,6 +369,11 @@ class ScriptLoadRequest
|
||||||
// LoadContext for augmenting the load depending on the loading
|
// LoadContext for augmenting the load depending on the loading
|
||||||
// context (DOM, Worker, etc.)
|
// context (DOM, Worker, etc.)
|
||||||
RefPtr<LoadContextBase> mLoadContext;
|
RefPtr<LoadContextBase> mLoadContext;
|
||||||
|
|
||||||
|
// EarlyHintRegistrar id to connect the http channel back to the preload, with
|
||||||
|
// a default of value of 0 indicating that this request is not an early hints
|
||||||
|
// preload.
|
||||||
|
uint64_t mEarlyHintPreloaderId;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScriptLoadRequestList : private mozilla::LinkedList<ScriptLoadRequest> {
|
class ScriptLoadRequestList : private mozilla::LinkedList<ScriptLoadRequest> {
|
||||||
|
|
|
||||||
|
|
@ -11773,8 +11773,7 @@
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
# Enables `<link rel="modulepreload">` tag and `Link: rel=modulepreload`
|
# Enables `<link rel="modulepreload">` tag and `Link: rel=modulepreload`
|
||||||
# response header handling. The latter is not yet implemented, see:
|
# response header handling.
|
||||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1773056.
|
|
||||||
- name: network.modulepreload
|
- name: network.modulepreload
|
||||||
type: RelaxedAtomicBool
|
type: RelaxedAtomicBool
|
||||||
value: true
|
value: true
|
||||||
|
|
|
||||||
|
|
@ -131,15 +131,18 @@ EarlyHintPreloader::~EarlyHintPreloader() {
|
||||||
/* static */
|
/* static */
|
||||||
Maybe<PreloadHashKey> EarlyHintPreloader::GenerateHashKey(
|
Maybe<PreloadHashKey> EarlyHintPreloader::GenerateHashKey(
|
||||||
ASDestination aAs, nsIURI* aURI, nsIPrincipal* aPrincipal,
|
ASDestination aAs, nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||||
CORSMode aCorsMode, const nsAString& aType) {
|
CORSMode aCorsMode, bool aIsModulepreload) {
|
||||||
|
if (aIsModulepreload) {
|
||||||
|
return Some(PreloadHashKey::CreateAsScript(
|
||||||
|
aURI, aCorsMode, JS::loader::ScriptKind::eModule));
|
||||||
|
}
|
||||||
if (aAs == ASDestination::DESTINATION_FONT && aCorsMode != CORS_NONE) {
|
if (aAs == ASDestination::DESTINATION_FONT && aCorsMode != CORS_NONE) {
|
||||||
return Some(PreloadHashKey::CreateAsFont(aURI, aCorsMode));
|
return Some(PreloadHashKey::CreateAsFont(aURI, aCorsMode));
|
||||||
}
|
}
|
||||||
if (aAs == ASDestination::DESTINATION_IMAGE) {
|
if (aAs == ASDestination::DESTINATION_IMAGE) {
|
||||||
return Some(PreloadHashKey::CreateAsImage(aURI, aPrincipal, aCorsMode));
|
return Some(PreloadHashKey::CreateAsImage(aURI, aPrincipal, aCorsMode));
|
||||||
}
|
}
|
||||||
if (aAs == ASDestination::DESTINATION_SCRIPT &&
|
if (aAs == ASDestination::DESTINATION_SCRIPT) {
|
||||||
!aType.LowerCaseEqualsASCII("module")) {
|
|
||||||
return Some(PreloadHashKey::CreateAsScript(
|
return Some(PreloadHashKey::CreateAsScript(
|
||||||
aURI, aCorsMode, JS::loader::ScriptKind::eClassic));
|
aURI, aCorsMode, JS::loader::ScriptKind::eClassic));
|
||||||
}
|
}
|
||||||
|
|
@ -156,8 +159,7 @@ Maybe<PreloadHashKey> EarlyHintPreloader::GenerateHashKey(
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
nsSecurityFlags EarlyHintPreloader::ComputeSecurityFlags(CORSMode aCORSMode,
|
nsSecurityFlags EarlyHintPreloader::ComputeSecurityFlags(CORSMode aCORSMode,
|
||||||
ASDestination aAs,
|
ASDestination aAs) {
|
||||||
bool aIsModule) {
|
|
||||||
if (aAs == ASDestination::DESTINATION_FONT) {
|
if (aAs == ASDestination::DESTINATION_FONT) {
|
||||||
return nsContentSecurityManager::ComputeSecurityFlags(
|
return nsContentSecurityManager::ComputeSecurityFlags(
|
||||||
CORSMode::CORS_NONE,
|
CORSMode::CORS_NONE,
|
||||||
|
|
@ -170,12 +172,6 @@ nsSecurityFlags EarlyHintPreloader::ComputeSecurityFlags(CORSMode aCORSMode,
|
||||||
nsILoadInfo::SEC_ALLOW_CHROME;
|
nsILoadInfo::SEC_ALLOW_CHROME;
|
||||||
}
|
}
|
||||||
if (aAs == ASDestination::DESTINATION_SCRIPT) {
|
if (aAs == ASDestination::DESTINATION_SCRIPT) {
|
||||||
if (aIsModule) {
|
|
||||||
return nsContentSecurityManager::ComputeSecurityFlags(
|
|
||||||
aCORSMode, nsContentSecurityManager::CORSSecurityMapping::
|
|
||||||
REQUIRE_CORS_CHECKS) |
|
|
||||||
nsILoadInfo::SEC_ALLOW_CHROME;
|
|
||||||
}
|
|
||||||
return nsContentSecurityManager::ComputeSecurityFlags(
|
return nsContentSecurityManager::ComputeSecurityFlags(
|
||||||
aCORSMode, nsContentSecurityManager::CORSSecurityMapping::
|
aCORSMode, nsContentSecurityManager::CORSSecurityMapping::
|
||||||
CORS_NONE_MAPS_TO_DISABLED_CORS_CHECKS) |
|
CORS_NONE_MAPS_TO_DISABLED_CORS_CHECKS) |
|
||||||
|
|
@ -205,7 +201,8 @@ void EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||||
nsIURI* aBaseURI, nsIPrincipal* aPrincipal,
|
nsIURI* aBaseURI, nsIPrincipal* aPrincipal,
|
||||||
nsICookieJarSettings* aCookieJarSettings,
|
nsICookieJarSettings* aCookieJarSettings,
|
||||||
const nsACString& aResponseReferrerPolicy, const nsACString& aCSPHeader,
|
const nsACString& aResponseReferrerPolicy, const nsACString& aCSPHeader,
|
||||||
uint64_t aBrowsingContextID, nsIInterfaceRequestor* aCallbacks) {
|
uint64_t aBrowsingContextID, nsIInterfaceRequestor* aCallbacks,
|
||||||
|
bool aIsModulepreload) {
|
||||||
nsAttrValue as;
|
nsAttrValue as;
|
||||||
ParseAsValue(aLinkHeader.mAs, as);
|
ParseAsValue(aLinkHeader.mAs, as);
|
||||||
|
|
||||||
|
|
@ -217,7 +214,7 @@ void EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (as.GetEnumValue() == ASDestination::DESTINATION_INVALID) {
|
if (destination == ASDestination::DESTINATION_INVALID && !aIsModulepreload) {
|
||||||
// return early when it's definitly not an asset type we preload
|
// return early when it's definitly not an asset type we preload
|
||||||
// would be caught later as well, e.g. when creating the PreloadHashKey
|
// would be caught later as well, e.g. when creating the PreloadHashKey
|
||||||
return;
|
return;
|
||||||
|
|
@ -242,8 +239,7 @@ void EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||||
CORSMode corsMode = dom::Element::StringToCORSMode(aLinkHeader.mCrossOrigin);
|
CORSMode corsMode = dom::Element::StringToCORSMode(aLinkHeader.mCrossOrigin);
|
||||||
|
|
||||||
Maybe<PreloadHashKey> hashKey =
|
Maybe<PreloadHashKey> hashKey =
|
||||||
GenerateHashKey(static_cast<ASDestination>(as.GetEnumValue()), uri,
|
GenerateHashKey(destination, uri, aPrincipal, corsMode, aIsModulepreload);
|
||||||
aPrincipal, corsMode, aLinkHeader.mType);
|
|
||||||
if (!hashKey) {
|
if (!hashKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +248,12 @@ void EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsContentPolicyType contentPolicyType = AsValueToContentPolicy(as);
|
nsContentPolicyType contentPolicyType =
|
||||||
|
aIsModulepreload ? (IsScriptLikeOrInvalid(aLinkHeader.mAs)
|
||||||
|
? nsContentPolicyType::TYPE_SCRIPT
|
||||||
|
: nsContentPolicyType::TYPE_INVALID)
|
||||||
|
: AsValueToContentPolicy(as);
|
||||||
|
|
||||||
if (contentPolicyType == nsContentPolicyType::TYPE_INVALID) {
|
if (contentPolicyType == nsContentPolicyType::TYPE_INVALID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -284,9 +285,29 @@ void EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||||
|
|
||||||
RefPtr<EarlyHintPreloader> earlyHintPreloader = new EarlyHintPreloader();
|
RefPtr<EarlyHintPreloader> earlyHintPreloader = new EarlyHintPreloader();
|
||||||
|
|
||||||
nsSecurityFlags securityFlags = EarlyHintPreloader::ComputeSecurityFlags(
|
DebugOnly<bool> result =
|
||||||
corsMode, static_cast<ASDestination>(as.GetEnumValue()),
|
aOngoingEarlyHints->Add(*hashKey, earlyHintPreloader);
|
||||||
aLinkHeader.mType.LowerCaseEqualsASCII("module"));
|
MOZ_ASSERT(result);
|
||||||
|
|
||||||
|
// Security flags for modulepreload's request mode are computed here directly
|
||||||
|
// until full support for worker destinations can be added.
|
||||||
|
//
|
||||||
|
// Implements "To fetch a single module script,"
|
||||||
|
// Step 9. If destination is "worker", "sharedworker", or "serviceworker",
|
||||||
|
// and the top-level module fetch flag is set, then set request's
|
||||||
|
// mode to "same-origin".
|
||||||
|
nsSecurityFlags securityFlags =
|
||||||
|
aIsModulepreload
|
||||||
|
? ((aLinkHeader.mAs.LowerCaseEqualsASCII("worker") ||
|
||||||
|
aLinkHeader.mAs.LowerCaseEqualsASCII("sharedworker") ||
|
||||||
|
aLinkHeader.mAs.LowerCaseEqualsASCII("serviceworker"))
|
||||||
|
? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
|
||||||
|
: nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT) |
|
||||||
|
(corsMode == CORS_USE_CREDENTIALS
|
||||||
|
? nsILoadInfo::SEC_COOKIES_INCLUDE
|
||||||
|
: nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) |
|
||||||
|
nsILoadInfo::SEC_ALLOW_CHROME
|
||||||
|
: EarlyHintPreloader::ComputeSecurityFlags(corsMode, destination);
|
||||||
|
|
||||||
// Verify that the resource should be loaded.
|
// Verify that the resource should be loaded.
|
||||||
// This isn't the ideal way to test the resource against the CSP.
|
// This isn't the ideal way to test the resource against the CSP.
|
||||||
|
|
@ -360,10 +381,6 @@ void EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||||
aCookieJarSettings, aBrowsingContextID, aCallbacks));
|
aCookieJarSettings, aBrowsingContextID, aCallbacks));
|
||||||
|
|
||||||
earlyHintPreloader->SetLinkHeader(aLinkHeader);
|
earlyHintPreloader->SetLinkHeader(aLinkHeader);
|
||||||
|
|
||||||
DebugOnly<bool> result =
|
|
||||||
aOngoingEarlyHints->Add(*hashKey, earlyHintPreloader);
|
|
||||||
MOZ_ASSERT(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult EarlyHintPreloader::OpenChannel(
|
nsresult EarlyHintPreloader::OpenChannel(
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,8 @@ class EarlyHintPreloader final : public nsIStreamListener,
|
||||||
nsIURI* aBaseURI, nsIPrincipal* aPrincipal,
|
nsIURI* aBaseURI, nsIPrincipal* aPrincipal,
|
||||||
nsICookieJarSettings* aCookieJarSettings,
|
nsICookieJarSettings* aCookieJarSettings,
|
||||||
const nsACString& aReferrerPolicy, const nsACString& aCSPHeader,
|
const nsACString& aReferrerPolicy, const nsACString& aCSPHeader,
|
||||||
uint64_t aBrowsingContextID, nsIInterfaceRequestor* aCallbacks);
|
uint64_t aBrowsingContextID, nsIInterfaceRequestor* aCallbacks,
|
||||||
|
bool aIsModulepreload);
|
||||||
|
|
||||||
// register Channel to EarlyHintRegistrar. Returns true and sets connect args
|
// register Channel to EarlyHintRegistrar. Returns true and sets connect args
|
||||||
// if successful
|
// if successful
|
||||||
|
|
@ -121,11 +122,10 @@ class EarlyHintPreloader final : public nsIStreamListener,
|
||||||
static Maybe<PreloadHashKey> GenerateHashKey(ASDestination aAs, nsIURI* aURI,
|
static Maybe<PreloadHashKey> GenerateHashKey(ASDestination aAs, nsIURI* aURI,
|
||||||
nsIPrincipal* aPrincipal,
|
nsIPrincipal* aPrincipal,
|
||||||
CORSMode corsMode,
|
CORSMode corsMode,
|
||||||
const nsAString& aType);
|
bool aIsModulepreload);
|
||||||
|
|
||||||
static nsSecurityFlags ComputeSecurityFlags(CORSMode aCORSMode,
|
static nsSecurityFlags ComputeSecurityFlags(CORSMode aCORSMode,
|
||||||
ASDestination aAs,
|
ASDestination aAs);
|
||||||
bool aIsModule);
|
|
||||||
|
|
||||||
// call to start the preload
|
// call to start the preload
|
||||||
nsresult OpenChannel(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
nsresult OpenChannel(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,13 @@ void EarlyHintsService::EarlyHint(const nsACString& aLinkHeader,
|
||||||
EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||||
mOngoingEarlyHints, linkHeader, aBaseURI, principal,
|
mOngoingEarlyHints, linkHeader, aBaseURI, principal,
|
||||||
cookieJarSettings, aReferrerPolicy, aCSPHeader,
|
cookieJarSettings, aReferrerPolicy, aCSPHeader,
|
||||||
loadInfo->GetBrowsingContextID(), aCallbacks);
|
loadInfo->GetBrowsingContextID(), aCallbacks, false);
|
||||||
|
} else if (linkHeader.mRel.LowerCaseEqualsLiteral("modulepreload")) {
|
||||||
|
mLinkType |= dom::LinkStyle::eMODULE_PRELOAD;
|
||||||
|
EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||||
|
mOngoingEarlyHints, linkHeader, aBaseURI, principal,
|
||||||
|
cookieJarSettings, aReferrerPolicy, aCSPHeader,
|
||||||
|
loadInfo->GetBrowsingContextID(), aCallbacks, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
[modulepreload-in-early-hints.h2.window.html]
|
|
||||||
[Modulepreload in an early hints.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
[link-header-modulepreload.html]
|
|
||||||
expected: TIMEOUT
|
|
||||||
[test that a header-preloaded module is loaded and consumed]
|
|
||||||
expected: TIMEOUT
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// META: script=/common/utils.js
|
||||||
|
// META: script=resources/early-hints-helpers.sub.js
|
||||||
|
|
||||||
|
// see modulepreload-in-early-hints.h2.window.js for params explanation
|
||||||
|
test(() => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set("description",
|
||||||
|
'Modulepreload should not load with as="worker" from cross-origin url');
|
||||||
|
params.set("resource-url",
|
||||||
|
CROSS_ORIGIN_RESOURCES_URL + "/empty.js?" + token());
|
||||||
|
params.set("as", "worker");
|
||||||
|
params.set("should-preload", false);
|
||||||
|
const test_url = "resources/modulepreload-in-early-hints.h2.py?" + params.toString();
|
||||||
|
window.location.replace(new URL(test_url, window.location));
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// META: script=/common/utils.js
|
||||||
|
// META: script=resources/early-hints-helpers.sub.js
|
||||||
|
|
||||||
|
// see modulepreload-in-early-hints.h2.window.js for params explanation
|
||||||
|
test(() => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set("description",
|
||||||
|
'Modulepreload should load with as="worker" from same-origin url');
|
||||||
|
params.set("resource-url",
|
||||||
|
SAME_ORIGIN_RESOURCES_URL + "/empty.js?" + token());
|
||||||
|
params.set("as", "worker");
|
||||||
|
params.set("should-preload", true);
|
||||||
|
const test_url = "resources/modulepreload-in-early-hints.h2.py?" + params.toString();
|
||||||
|
window.location.replace(new URL(test_url, window.location));
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// META: script=/common/utils.js
|
||||||
|
// META: script=resources/early-hints-helpers.sub.js
|
||||||
|
|
||||||
|
// see modulepreload-in-early-hints.h2.window.js for params explanation
|
||||||
|
test(() => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set("description", "Modulepreload works in early hints from cross-origin url");
|
||||||
|
params.set("resource-url",
|
||||||
|
CROSS_ORIGIN_RESOURCES_URL + "/empty.js?" + token());
|
||||||
|
params.set("should-preload", true);
|
||||||
|
const test_url = "resources/modulepreload-in-early-hints.h2.py?" + params.toString();
|
||||||
|
window.location.replace(new URL(test_url, window.location));
|
||||||
|
});
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
// META: script=/common/utils.js
|
// META: script=/common/utils.js
|
||||||
// META: script=resources/early-hints-helpers.sub.js
|
// META: script=resources/early-hints-helpers.sub.js
|
||||||
|
|
||||||
|
// params are sent to a Python handler[1] that returns a 103 Early Hints
|
||||||
|
// response based the values of "resource-url" and "as", and then that response
|
||||||
|
// is validated by a window test[2] according to the value of "should-preload"
|
||||||
|
//
|
||||||
|
// see: https://web-platform-tests.org/writing-tests/h2tests.html
|
||||||
|
//
|
||||||
|
// [1]: resources/modulepreload-in-early-hints.h2.py
|
||||||
|
// [2]: resources/modulepreload-in-early-hints.h2.html
|
||||||
test(() => {
|
test(() => {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
|
params.set("description", "Modulepreload works in early hints");
|
||||||
params.set("resource-url",
|
params.set("resource-url",
|
||||||
SAME_ORIGIN_RESOURCES_URL + "/empty.js?" + token());
|
SAME_ORIGIN_RESOURCES_URL + "/empty.js?" + token());
|
||||||
|
params.set("should-preload", true);
|
||||||
const test_url = "resources/modulepreload-in-early-hints.h2.py?" + params.toString();
|
const test_url = "resources/modulepreload-in-early-hints.h2.py?" + params.toString();
|
||||||
window.location.replace(new URL(test_url, window.location));
|
window.location.replace(new URL(test_url, window.location));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,12 @@ import os
|
||||||
|
|
||||||
def handle_headers(frame, request, response):
|
def handle_headers(frame, request, response):
|
||||||
resource_url = request.GET.first(b"resource-url").decode()
|
resource_url = request.GET.first(b"resource-url").decode()
|
||||||
link_header_value = "<{}>; rel=modulepreload".format(resource_url)
|
as_value = request.GET.first(b"as", None)
|
||||||
|
if as_value:
|
||||||
|
link_header_value = "<{}>; rel=modulepreload; as={}".format(
|
||||||
|
resource_url, as_value.decode())
|
||||||
|
else:
|
||||||
|
link_header_value = "<{}>; rel=modulepreload".format(resource_url)
|
||||||
early_hints = [
|
early_hints = [
|
||||||
(b":status", b"103"),
|
(b":status", b"103"),
|
||||||
(b"link", link_header_value),
|
(b"link", link_header_value),
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,14 @@ async function fetchModuleScript(url) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const description = params.get("description");
|
||||||
|
|
||||||
promise_test(async (t) => {
|
promise_test(async (t) => {
|
||||||
const params = new URLSearchParams(window.location.search);
|
|
||||||
const resource_url = params.get("resource-url");
|
const resource_url = params.get("resource-url");
|
||||||
|
const should_preload = params.get("should-preload") === "true";
|
||||||
await fetchModuleScript(resource_url);
|
await fetchModuleScript(resource_url);
|
||||||
assert_true(isPreloadedByEarlyHints(resource_url));
|
assert_equals(isPreloadedByEarlyHints(resource_url), should_preload);
|
||||||
}, "Modulepreload in an early hints.");
|
}, description);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue