diff --git a/dom/html/HTMLLinkElement.cpp b/dom/html/HTMLLinkElement.cpp index c9d64d1f3872d..9311f836be969 100644 --- a/dom/html/HTMLLinkElement.cpp +++ b/dom/html/HTMLLinkElement.cpp @@ -9,7 +9,6 @@ #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/Attributes.h" #include "mozilla/Components.h" -#include "mozilla/ConsoleReportCollector.h" #include "mozilla/EventDispatcher.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Preferences.h" @@ -43,7 +42,6 @@ #include "DecoderTraits.h" #include "MediaList.h" #include "nsAttrValueInlines.h" -#include "js/loader/ModuleLoadRequest.h" NS_IMPL_NS_NEW_HTML_ELEMENT(Link) @@ -314,30 +312,36 @@ nsresult HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify); } -// Keep this and the arrays below in sync with ToLinkMask in LinkStyle.cpp. -#define SUPPORTED_REL_VALUES_BASE \ - "prefetch", "dns-prefetch", "stylesheet", "next", "alternate", "preconnect", \ - "icon", "search", nullptr - -static const DOMTokenListSupportedToken sSupportedRelValueCombinations[][12] = { - {SUPPORTED_REL_VALUES_BASE}, - {"manifest", SUPPORTED_REL_VALUES_BASE}, - {"preload", SUPPORTED_REL_VALUES_BASE}, - {"preload", "manifest", SUPPORTED_REL_VALUES_BASE}, - {"modulepreload", SUPPORTED_REL_VALUES_BASE}, - {"modulepreload", "manifest", SUPPORTED_REL_VALUES_BASE}, - {"modulepreload", "preload", SUPPORTED_REL_VALUES_BASE}, - {"modulepreload", "preload", "manifest", SUPPORTED_REL_VALUES_BASE}}; -#undef SUPPORTED_REL_VALUES_BASE +static const DOMTokenListSupportedToken sSupportedRelValues[] = { + // Keep this and the one below in sync with ToLinkMask in + // LinkStyle.cpp. + // "preload" must come first because it can be disabled. + "preload", "prefetch", "dns-prefetch", "stylesheet", + "next", "alternate", "preconnect", "icon", + "search", "modulepreload", nullptr}; + +static const DOMTokenListSupportedToken sSupportedRelValuesWithManifest[] = { + // Keep this in sync with ToLinkMask in LinkStyle.cpp. + // "preload" and "manifest" must come first because they can be disabled. + "preload", "manifest", "prefetch", "dns-prefetch", "stylesheet", "next", + "alternate", "preconnect", "icon", "search", nullptr}; nsDOMTokenList* HTMLLinkElement::RelList() { if (!mRelList) { - int index = (StaticPrefs::dom_manifest_enabled() ? 1 : 0) | - (StaticPrefs::network_preload() ? 2 : 0) | - (StaticPrefs::network_modulepreload() ? 4 : 0); - - mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, - sSupportedRelValueCombinations[index]); + auto preload = StaticPrefs::network_preload(); + auto manifest = StaticPrefs::dom_manifest_enabled(); + if (manifest && preload) { + mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, + sSupportedRelValuesWithManifest); + } else if (manifest && !preload) { + mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, + &sSupportedRelValuesWithManifest[1]); + } else if (!manifest && preload) { + mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, sSupportedRelValues); + } else { // both false...drop preload + mRelList = + new nsDOMTokenList(this, nsGkAtoms::rel, &sSupportedRelValues[1]); + } } return mRelList; } @@ -487,10 +491,7 @@ void HTMLLinkElement:: } if (linkTypes & eMODULE_PRELOAD) { - ScriptLoader* scriptLoader = OwnerDoc()->ScriptLoader(); - ModuleLoader* moduleLoader = scriptLoader->GetModuleLoader(); - - if (!moduleLoader) { + if (!OwnerDoc()->ScriptLoader()->GetModuleLoader()) { // For the print preview documents, at this moment it doesn't have module // loader yet, as the (print preview) document is not attached to the // nsIContentViewer yet, so it doesn't have the GlobalObject. @@ -500,54 +501,9 @@ void HTMLLinkElement:: return; } - if (!StaticPrefs::network_modulepreload()) { - // Keep behavior from https://phabricator.services.mozilla.com/D149371, - // prior to main implementation of modulepreload - moduleLoader->DisallowImportMaps(); - return; - } - - // https://html.spec.whatwg.org/multipage/semantics.html#processing-the-media-attribute - // TODO: apply this check for all linkTypes - nsAutoString media; - if (GetAttr(kNameSpaceID_None, nsGkAtoms::media, media)) { - RefPtr mediaList = - mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(media)); - if (!mediaList->Matches(*OwnerDoc())) { - return; - } - } - - // TODO: per spec, apply this check for ePREFETCH as well - if (!HasNonEmptyAttr(nsGkAtoms::href)) { - return; - } - - nsAutoString destination; - GetAs(destination); - if (!(destination.IsEmpty() || - destination.LowerCaseEqualsLiteral("audioworklet") || - destination.LowerCaseEqualsLiteral("paintworklet") || - destination.LowerCaseEqualsLiteral("script") || - destination.LowerCaseEqualsLiteral("serviceworker") || - destination.LowerCaseEqualsLiteral("sharedworker") || - destination.LowerCaseEqualsLiteral("worker"))) { - RefPtr asyncDispatcher = new AsyncEventDispatcher( - this, u"error"_ns, CanBubble::eNo, ChromeOnlyDispatch::eNo); - asyncDispatcher->PostDOMEvent(); - return; - } - - nsCOMPtr uri = GetURI(); - if (!uri) { - return; - } - // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-modulepreload-module-script-graph // Step 1. Disallow further import maps given settings object. - moduleLoader->DisallowImportMaps(); - - StartPreload(nsIContentPolicy::TYPE_SCRIPT); + OwnerDoc()->ScriptLoader()->GetModuleLoader()->DisallowImportMaps(); return; } diff --git a/dom/script/ScriptLoadContext.h b/dom/script/ScriptLoadContext.h index 61aa074667000..a15163c90a8d5 100644 --- a/dom/script/ScriptLoadContext.h +++ b/dom/script/ScriptLoadContext.h @@ -62,10 +62,9 @@ class Element; * the script does not need to be fetched first. * * mIsXSLT * Set if we are in an XSLT request. - * * mIsPreload + * * TODO: mIsPreload (will be moved from ScriptFetchOptions) * Set for scripts that are preloaded in a - * or - * element. + * element. * * In addition to describing how the ScriptLoadRequest will be loaded by the * DOM ScriptLoader, the ScriptLoadContext contains fields that facilitate @@ -114,7 +113,7 @@ class ScriptLoadContext : public JS::loader::LoadContextBase, eDeferred, eAsync, eLinkPreload // this is a load initiated by or tag + // as="script"> tag }; void SetScriptMode(bool aDeferAttr, bool aAsyncAttr, bool aLinkPreload); diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index ebaad9c5e3b5e..ca076fd839ea3 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -675,12 +675,12 @@ nsresult ScriptLoader::StartLoadInternal( aRequest->GetScriptLoadContext()->IsTracking())); if (aRequest->GetScriptLoadContext()->IsLinkPreloadScript()) { - // This is or - // initiated speculative load, put it to the group that is not blocked by - // leaders and doesn't block follower at the same time. Giving it a much - // higher priority will make this request be processed ahead of other - // Unblocked requests, but with the same weight as Leaders. This will make - // us behave similar way for both http2 and http1. + // This is initiated speculative load, + // put it to the group that is not blocked by leaders and doesn't block + // follower at the same time. Giving it a much higher priority will make + // this request be processed ahead of other Unblocked requests, but with + // the same weight as Leaders. This will make us behave similar way for + // both http2 and http1. ScriptLoadContext::PrioritizeAsPreload(channel); ScriptLoadContext::AddLoadBackgroundFlag(channel); } else if (nsCOMPtr cos = do_QueryInterface(channel)) { @@ -786,8 +786,7 @@ nsresult ScriptLoader::StartLoadInternal( aRequest->mURI, aRequest->CORSMode(), aRequest->mKind); aRequest->GetScriptLoadContext()->NotifyOpen( key, channel, mDocument, - aRequest->GetScriptLoadContext()->IsLinkPreloadScript(), - aRequest->IsModuleRequest()); + aRequest->GetScriptLoadContext()->IsLinkPreloadScript()); if (aEarlyHintPreloaderId) { nsCOMPtr channelInternal = diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 7deb65268bad0..fdd2a563c5720 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -11616,21 +11616,12 @@ value: false mirror: always -# Enables `` tag and `Link: rel=preload` response header -# handling. +# Enables `` tag and `Link: rel=preload` response header handling. - name: network.preload type: RelaxedAtomicBool value: true mirror: always -# Enables `` tag and `Link: rel=modulepreload` -# response header handling. The latter is not yet implemented, see: -# https://bugzilla.mozilla.org/show_bug.cgi?id=1773056. -- name: network.modulepreload - type: RelaxedAtomicBool - value: true - mirror: always - # Enable 103 Early Hint status code (RFC 8297) - name: network.early-hints.enabled type: RelaxedAtomicBool diff --git a/parser/html/nsHtml5TreeBuilderCppSupplement.h b/parser/html/nsHtml5TreeBuilderCppSupplement.h index 3e35d43017b52..ce8733c1d0db6 100644 --- a/parser/html/nsHtml5TreeBuilderCppSupplement.h +++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h @@ -336,27 +336,6 @@ nsIContentHandle* nsHtml5TreeBuilder::createElement( } // Other "as" values will be supported later. } - } else if (mozilla::StaticPrefs::network_modulepreload() && - rel.LowerCaseEqualsASCII("modulepreload")) { - nsHtml5String url = - aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); - if (url && url.Length() != 0) { - nsHtml5String charset = - aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET); - nsHtml5String type = nsHtml5String::FromLiteral("module"); - nsHtml5String crossOrigin = aAttributes->getValue( - nsHtml5AttributeName::ATTR_CROSSORIGIN); - nsHtml5String media = - aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA); - nsHtml5String integrity = - aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); - nsHtml5String referrerPolicy = aAttributes->getValue( - nsHtml5AttributeName::ATTR_REFERRERPOLICY); - mSpeculativeLoadQueue.AppendElement()->InitScript( - url, charset, type, crossOrigin, media, integrity, - referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD, false, - false, false, true); - } } } } else if (nsGkAtoms::video == aName) { diff --git a/testing/web-platform/meta/preload/avoid-delaying-onload-link-modulepreload-exec.html.ini b/testing/web-platform/meta/preload/avoid-delaying-onload-link-modulepreload-exec.html.ini new file mode 100644 index 0000000000000..597d1953dffc5 --- /dev/null +++ b/testing/web-platform/meta/preload/avoid-delaying-onload-link-modulepreload-exec.html.ini @@ -0,0 +1,2 @@ +[avoid-delaying-onload-link-modulepreload-exec.html] + expected: ERROR diff --git a/testing/web-platform/meta/preload/avoid-delaying-onload-link-modulepreload.html.ini b/testing/web-platform/meta/preload/avoid-delaying-onload-link-modulepreload.html.ini new file mode 100644 index 0000000000000..e43638d861565 --- /dev/null +++ b/testing/web-platform/meta/preload/avoid-delaying-onload-link-modulepreload.html.ini @@ -0,0 +1,2 @@ +[avoid-delaying-onload-link-modulepreload.html] + expected: ERROR diff --git a/testing/web-platform/meta/preload/modulepreload.html.ini b/testing/web-platform/meta/preload/modulepreload.html.ini new file mode 100644 index 0000000000000..46bda70a855e9 --- /dev/null +++ b/testing/web-platform/meta/preload/modulepreload.html.ini @@ -0,0 +1,47 @@ +[modulepreload.html] + expected: TIMEOUT + [link rel=modulepreload] + expected: TIMEOUT + + [link rel=modulepreload with submodules] + expected: NOTRUN + + [link rel=modulepreload for a module with syntax error] + expected: NOTRUN + + [link rel=modulepreload for a module with network error] + expected: NOTRUN + + [link rel=modulepreload with bad href attribute] + expected: NOTRUN + + [link rel=modulepreload as=script] + expected: NOTRUN + + [link rel=modulepreload with invalid as= value] + expected: NOTRUN + + [link rel=modulepreload with integrity match] + expected: NOTRUN + + [link rel=modulepreload with integrity mismatch] + expected: NOTRUN + + [cross-origin link rel=modulepreload] + expected: NOTRUN + + [cross-origin link rel=modulepreload crossorigin=anonymous] + expected: NOTRUN + + [same-origin link rel=modulepreload crossorigin=anonymous] + expected: NOTRUN + + [same-origin link rel=modulepreload crossorigin=use-credentials] + expected: NOTRUN + + [cross-origin link rel=modulepreload crossorigin=use-credentials] + expected: NOTRUN + + [link rel=modulepreload with integrity match2] + expected: NOTRUN + diff --git a/testing/web-platform/tests/preload/modulepreload-sri.html b/testing/web-platform/tests/preload/modulepreload-sri.html deleted file mode 100644 index f7ff8548a61ed..0000000000000 --- a/testing/web-platform/tests/preload/modulepreload-sri.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - diff --git a/testing/web-platform/tests/preload/modulepreload.html b/testing/web-platform/tests/preload/modulepreload.html index ef799742590bd..0e4b6923e32e8 100644 --- a/testing/web-platform/tests/preload/modulepreload.html +++ b/testing/web-platform/tests/preload/modulepreload.html @@ -33,15 +33,6 @@ }); } -function attachAndWaitForTimeout(element, t) { - return new Promise((resolve, reject) => { - element.onload = reject; - element.onerror = reject; - t.step_timeout(resolve, 1000); - document.body.appendChild(element); - }); -} - promise_test(function(t) { var link = document.createElement('link'); link.rel = 'modulepreload'; @@ -229,7 +220,7 @@ var link = document.createElement('link'); link.rel = 'modulepreload'; link.href = 'resources/module1.js?integrity-match'; - link.integrity = 'sha256-+Ks3iNIiTq2ujlWhvB056cmXobrCFpU9hd60xZ1WCaA=' + link.integrity = 'sha256-dOxReWMnMSPfUvxEbBqIrjNh8ZN8n05j7h3JmhF8gQc=%' return attachAndWaitForLoad(link); }, 'link rel=modulepreload with integrity match'); @@ -237,7 +228,7 @@ var link = document.createElement('link'); link.rel = 'modulepreload'; link.href = 'resources/module1.mjs?integrity-match'; - link.integrity = 'sha256-+Ks3iNIiTq2ujlWhvB056cmXobrCFpU9hd60xZ1WCaA=' + link.integrity = 'sha256-dOxReWMnMSPfUvxEbBqIrjNh8ZN8n05j7h3JmhF8gQc=%' return attachAndWaitForLoad(link); }, 'link rel=modulepreload with integrity match2'); @@ -249,88 +240,5 @@ return attachAndWaitForError(link); }, 'link rel=modulepreload with integrity mismatch'); -promise_test(function(t) { - var link = document.createElement('link'); - link.rel = 'modulepreload'; - link.href = 'resources/module1.mjs?integrity-doesnotmatch'; - link.integrity = 'sha256-dOxReWMnMSPfUvxEbBqIrjNh8ZN8n05j7h3JmhF8gQc=' - return attachAndWaitForError(link); -}, 'link rel=modulepreload with integrity mismatch2'); - -promise_test(function(t) { - var link = document.createElement('link'); - link.rel = 'modulepreload'; - link.href = 'resources/module1.mjs?integrity-invalid'; - link.integrity = 'sha256-dOxReWMnMSPfUvxEbBqIrjNh8ZN8n05j7h3JmhF8gQc=%' - return attachAndWaitForError(link); -}, 'link rel=modulepreload with integrity mismatch3'); - -promise_test(function(t) { - var link1 = document.createElement('link'); - var link2 = document.createElement('link'); - link1.rel = 'modulepreload'; - link2.rel = 'modulepreload'; - link1.href = 'resources/module1.js?same-url'; - link2.href = 'resources/module1.js?same-url'; - return Promise.all([ - attachAndWaitForLoad(link1), - attachAndWaitForLoad(link2), - ]); -}, 'multiple link rel=modulepreload with same href'); - -promise_test(function(t) { - var link1 = document.createElement('link'); - var link2 = document.createElement('link'); - link1.rel = 'modulepreload'; - link2.rel = 'modulepreload'; - link1.href = 'resources/module2.js?child-before'; - link2.href = 'resources/module1.js?child-before'; - return attachAndWaitForLoad(link1) - .then(() => attachAndWaitForLoad(link2)) - .then(() => new Promise(r => t.step_timeout(r, 1000))) - .then(() => { - verifyNumberOfDownloads('resources/module2.js?child-before', 1); - }); - -}, 'multiple link rel=modulepreload with child module before parent'); - -promise_test(function(t) { - var link = document.createElement('link'); - link.rel = 'modulepreload'; - link.href = 'resources/module1.mjs?matching-media'; - link.media = 'all'; - return attachAndWaitForLoad(link); -}, 'link rel=modulepreload with matching media'); - -promise_test(function(t) { - var link = document.createElement('link'); - link.rel = 'modulepreload'; - link.href = 'resources/module1.mjs?non-matching-media'; - link.media = 'not all'; - return attachAndWaitForTimeout(link, t); -}, 'link rel=modulepreload with non-matching media'); - -promise_test(function(t) { - var link = document.createElement('link'); - link.rel = 'modulepreload'; - link.href = 'resources/module1.mjs?empty-media'; - link.media = ''; - return attachAndWaitForLoad(link); -}, 'link rel=modulepreload with empty media'); - -promise_test(function(t) { - var link = document.createElement('link'); - link.rel = 'modulepreload'; - link.href = ''; - return attachAndWaitForTimeout(link, t); -}, 'link rel=modulepreload with empty href'); - -promise_test(function(t) { - var link = document.createElement('link'); - link.rel = 'modulepreload'; - link.href = ''; - link.as = 'fetch'; - return attachAndWaitForTimeout(link, t); -}, 'link rel=modulepreload with empty href and invalid as= value'); diff --git a/uriloader/preload/PreloadService.cpp b/uriloader/preload/PreloadService.cpp index e10d542caf7b0..ddc3ba1d3bd89 100644 --- a/uriloader/preload/PreloadService.cpp +++ b/uriloader/preload/PreloadService.cpp @@ -72,10 +72,11 @@ already_AddRefed PreloadService::PreloadLinkElement( return nullptr; } - nsAutoString as, charset, crossOrigin, integrity, referrerPolicy, rel, srcset, + nsAutoString as, charset, crossOrigin, integrity, referrerPolicy, srcset, sizes, type, url; nsCOMPtr uri = aLinkElement->GetURI(); + aLinkElement->GetAs(as); aLinkElement->GetCharset(charset); aLinkElement->GetImageSrcset(srcset); aLinkElement->GetImageSizes(sizes); @@ -83,15 +84,7 @@ already_AddRefed PreloadService::PreloadLinkElement( aLinkElement->GetCrossOrigin(crossOrigin); aLinkElement->GetIntegrity(integrity); aLinkElement->GetReferrerPolicy(referrerPolicy); - aLinkElement->GetRel(rel); - - if (rel.LowerCaseEqualsASCII("modulepreload")) { - as = u"script"_ns; - type = u"module"_ns; - } else { - aLinkElement->GetAs(as); - aLinkElement->GetType(type); - } + aLinkElement->GetType(type); auto result = PreloadOrCoalesce(uri, url, aPolicyType, as, type, charset, srcset, sizes, integrity, crossOrigin, diff --git a/uriloader/preload/PreloaderBase.cpp b/uriloader/preload/PreloaderBase.cpp index 6c53e68d81a78..d61694e38139c 100644 --- a/uriloader/preload/PreloaderBase.cpp +++ b/uriloader/preload/PreloaderBase.cpp @@ -124,8 +124,7 @@ void PreloaderBase::AddLoadBackgroundFlag(nsIChannel* aChannel) { } void PreloaderBase::NotifyOpen(const PreloadHashKey& aKey, - dom::Document* aDocument, bool aIsPreload, - bool aIsModule) { + dom::Document* aDocument, bool aIsPreload) { if (aDocument) { DebugOnly alreadyRegistered = aDocument->Preloads().RegisterPreload(aKey, this); @@ -138,8 +137,7 @@ void PreloaderBase::NotifyOpen(const PreloadHashKey& aKey, mKey = aKey; mIsUsed = !aIsPreload; - // Start usage timer for rel="preload" but not for rel="modulepreload" - if (!aIsModule && !mIsUsed && !mUsageTimer) { + if (!mIsUsed && !mUsageTimer) { auto callback = MakeRefPtr(this, aDocument); NS_NewTimerWithCallback(getter_AddRefs(mUsageTimer), callback, 10000, nsITimer::TYPE_ONE_SHOT); @@ -149,9 +147,8 @@ void PreloaderBase::NotifyOpen(const PreloadHashKey& aKey, } void PreloaderBase::NotifyOpen(const PreloadHashKey& aKey, nsIChannel* aChannel, - dom::Document* aDocument, bool aIsPreload, - bool aIsModule) { - NotifyOpen(aKey, aDocument, aIsPreload, aIsModule); + dom::Document* aDocument, bool aIsPreload) { + NotifyOpen(aKey, aDocument, aIsPreload); mChannel = aChannel; nsCOMPtr callbacks; diff --git a/uriloader/preload/PreloaderBase.h b/uriloader/preload/PreloaderBase.h index ec7bd6b42f81d..18be5664fa85b 100644 --- a/uriloader/preload/PreloaderBase.h +++ b/uriloader/preload/PreloaderBase.h @@ -45,10 +45,9 @@ class PreloaderBase : public SupportsWeakPtr, public nsISupports { // preload service to provide coalescing, and access to the preload when it // should be used for an actual load. void NotifyOpen(const PreloadHashKey& aKey, dom::Document* aDocument, - bool aIsPreload, bool aIsModule = false); + bool aIsPreload); void NotifyOpen(const PreloadHashKey& aKey, nsIChannel* aChannel, - dom::Document* aDocument, bool aIsPreload, - bool aIsModule = false); + dom::Document* aDocument, bool aIsPreload); // Called when the load is about to be started all over again and thus this // PreloaderBase will be registered again with the same key. This method