commit a5efa5c5014ee194c2b79095f216e223a54e9aef
parent 57d3ff4e0713684663d8002b8b902a8aba4f74a0
Author: Edgar Chen <echen@mozilla.com>
Date: Fri, 19 Dec 2025 09:50:02 +0000
Bug 2003896 - Do not speculatively load preload image if the type is not supported; r=hsivonen
Differential Revision: https://phabricator.services.mozilla.com/D277117
Diffstat:
7 files changed, 97 insertions(+), 18 deletions(-)
diff --git a/parser/html/nsHtml5SpeculativeLoad.cpp b/parser/html/nsHtml5SpeculativeLoad.cpp
@@ -45,7 +45,8 @@ void nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor) {
aExecutor->PreloadImage(
mUrlOrSizes, mCrossOrigin, mMedia, mCharsetOrSrcset,
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity,
- mReferrerPolicyOrIntegrity, mIsLinkPreload, mFetchPriority);
+ mReferrerPolicyOrIntegrity, mIsLinkPreload, mFetchPriority,
+ mNonceOrType);
break;
case eSpeculativeLoadOpenPicture:
aExecutor->PreloadOpenPicture();
@@ -63,7 +64,7 @@ void nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor) {
aExecutor->PreloadScript(
mUrlOrSizes, mCharsetOrSrcset,
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity,
- mCrossOrigin, mMedia, mNonce, mFetchPriority,
+ mCrossOrigin, mMedia, mNonceOrType, mFetchPriority,
mReferrerPolicyOrIntegrity, mScriptReferrerPolicy, false, mIsAsync,
mIsDefer, mIsLinkPreload);
break;
@@ -71,14 +72,14 @@ void nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor) {
aExecutor->PreloadScript(
mUrlOrSizes, mCharsetOrSrcset,
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity,
- mCrossOrigin, mMedia, mNonce, mFetchPriority,
+ mCrossOrigin, mMedia, mNonceOrType, mFetchPriority,
mReferrerPolicyOrIntegrity, mScriptReferrerPolicy, true, mIsAsync,
mIsDefer, mIsLinkPreload);
break;
case eSpeculativeLoadStyle:
aExecutor->PreloadStyle(
mUrlOrSizes, mCharsetOrSrcset, mCrossOrigin, mMedia,
- mReferrerPolicyOrIntegrity, mNonce,
+ mReferrerPolicyOrIntegrity, mNonceOrType,
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity,
mIsLinkPreload, mFetchPriority);
break;
diff --git a/parser/html/nsHtml5SpeculativeLoad.h b/parser/html/nsHtml5SpeculativeLoad.h
@@ -75,7 +75,8 @@ class nsHtml5SpeculativeLoad {
inline void InitImage(nsHtml5String aUrl, nsHtml5String aCrossOrigin,
nsHtml5String aMedia, nsHtml5String aReferrerPolicy,
nsHtml5String aSrcset, nsHtml5String aSizes,
- bool aLinkPreload, nsHtml5String aFetchPriority) {
+ bool aLinkPreload, nsHtml5String aFetchPriority,
+ nsHtml5String aType) {
MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized,
"Trying to reinitialize a speculative load!");
mOpCode = eSpeculativeLoadImage;
@@ -93,6 +94,7 @@ class nsHtml5SpeculativeLoad {
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity);
mIsLinkPreload = aLinkPreload;
aFetchPriority.ToString(mFetchPriority);
+ aType.ToString(mNonceOrType);
}
inline void InitFont(nsHtml5String aUrl, nsHtml5String aCrossOrigin,
@@ -184,7 +186,7 @@ class nsHtml5SpeculativeLoad {
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity);
aCrossOrigin.ToString(mCrossOrigin);
aMedia.ToString(mMedia);
- aNonce.ToString(mNonce);
+ aNonce.ToString(mNonceOrType);
aFetchPriority.ToString(mFetchPriority);
aIntegrity.ToString(mReferrerPolicyOrIntegrity);
nsAutoString referrerPolicy;
@@ -210,7 +212,7 @@ class nsHtml5SpeculativeLoad {
mCrossOrigin.SetIsVoid(true);
mMedia.SetIsVoid(true);
mReferrerPolicyOrIntegrity.SetIsVoid(true);
- mNonce.SetIsVoid(true);
+ mNonceOrType.SetIsVoid(true);
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.SetIsVoid(
true);
}
@@ -233,7 +235,7 @@ class nsHtml5SpeculativeLoad {
mReferrerPolicyOrIntegrity.Assign(
nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
referrerPolicy));
- aNonce.ToString(mNonce);
+ aNonce.ToString(mNonceOrType);
aIntegrity.ToString(
mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity);
mIsLinkPreload = aLinkPreload;
@@ -410,10 +412,11 @@ class nsHtml5SpeculativeLoad {
*/
nsString mMedia;
/**
- * If mOpCode is eSpeculativeLoadScript[FromHead] this represents the value
- * of the "nonce" attribute.
+ * If mOpCode is eSpeculativeLoadImage this represents the value of the "type"
+ * attribute. If the attribute is not set, this will be a void string.
+ * Otherwise, it is empty or the value of the "nonce" attribute.
*/
- nsString mNonce;
+ nsString mNonceOrType;
/**
* If mOpCode is eSpeculativeLoadNoModuleScript[FromHead] or
* eSpeculativeLoadScript[FromHead] this represents the value of the
diff --git a/parser/html/nsHtml5TreeBuilderCppSupplement.h b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -220,9 +220,11 @@ nsIContentHandle* nsHtml5TreeBuilder::createElement(
aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES);
nsHtml5String fetchPriority =
aAttributes->getValue(nsHtml5AttributeName::ATTR_FETCHPRIORITY);
+ nsHtml5String type =
+ aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
mSpeculativeLoadQueue.AppendElement()->InitImage(
url, crossOrigin, /* aMedia = */ nullptr, referrerPolicy,
- srcset, sizes, false, fetchPriority);
+ srcset, sizes, false, fetchPriority, type);
}
} else if (nsGkAtoms::source == aName) {
nsHtml5String srcset =
@@ -439,9 +441,11 @@ nsIContentHandle* nsHtml5TreeBuilder::createElement(
nsHtml5AttributeName::ATTR_IMAGESRCSET);
nsHtml5String sizes = aAttributes->getValue(
nsHtml5AttributeName::ATTR_IMAGESIZES);
+ nsHtml5String type =
+ aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
mSpeculativeLoadQueue.AppendElement()->InitImage(
url, crossOrigin, media, referrerPolicy, srcset, sizes,
- true, fetchPriority);
+ true, fetchPriority, type);
} else if (as.LowerCaseEqualsASCII("font")) {
mSpeculativeLoadQueue.AppendElement()->InitFont(
url, crossOrigin, media, referrerPolicy, fetchPriority);
@@ -500,7 +504,7 @@ nsIContentHandle* nsHtml5TreeBuilder::createElement(
mSpeculativeLoadQueue.AppendElement()->InitImage(
url, nullptr, nullptr, nullptr, nullptr, nullptr, false,
- fetchPriority);
+ fetchPriority, nullptr);
}
} else if (nsGkAtoms::style == aName) {
mImportScanner.Start();
@@ -562,7 +566,7 @@ nsIContentHandle* nsHtml5TreeBuilder::createElement(
mSpeculativeLoadQueue.AppendElement()->InitImage(
url, crossOrigin, /* aMedia = */ nullptr, nullptr, nullptr,
- nullptr, false, fetchPriority);
+ nullptr, false, fetchPriority, nullptr);
}
} else if (nsGkAtoms::script == aName) {
nsHtml5TreeOperation* treeOp =
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -13,6 +13,7 @@
#include "mozilla/dom/nsCSPService.h"
#include "mozilla/dom/PolicyContainer.h"
+#include "imgLoader.h"
#include "mozAutoDocUpdate.h"
#include "mozilla/IdleTaskRunner.h"
#include "mozilla/Preferences.h"
@@ -1167,6 +1168,14 @@ bool nsHtml5TreeOpExecutor::ShouldPreloadURI(nsIURI* aURI) {
return mPreloadedURLs.EnsureInserted(spec);
}
+bool nsHtml5TreeOpExecutor::ImageTypeSupports(const nsAString& aType) {
+ if (aType.IsEmpty()) {
+ return true;
+ }
+ return imgLoader::SupportImageWithMimeType(
+ NS_ConvertUTF16toUTF8(aType), AcceptedMimeTypes::IMAGES_AND_DOCUMENTS);
+}
+
dom::ReferrerPolicy nsHtml5TreeOpExecutor::GetPreloadReferrerPolicy(
const nsAString& aReferrerPolicy) {
dom::ReferrerPolicy referrerPolicy =
@@ -1239,12 +1248,13 @@ void nsHtml5TreeOpExecutor::PreloadImage(
const nsAString& aURL, const nsAString& aCrossOrigin,
const nsAString& aMedia, const nsAString& aSrcset, const nsAString& aSizes,
const nsAString& aImageReferrerPolicy, bool aLinkPreload,
- const nsAString& aFetchPriority) {
+ const nsAString& aFetchPriority, const nsAString& aType) {
nsCOMPtr<nsIURI> baseURI = BaseURIForPreload();
bool isImgSet = false;
nsCOMPtr<nsIURI> uri =
mDocument->ResolvePreloadImage(baseURI, aURL, aSrcset, aSizes, &isImgSet);
- if (uri && ShouldPreloadURI(uri) && MediaApplies(aMedia)) {
+ if (uri && ShouldPreloadURI(uri) && MediaApplies(aMedia) &&
+ ImageTypeSupports(aType)) {
// use document wide referrer policy
mDocument->MaybePreLoadImage(uri, aCrossOrigin,
GetPreloadReferrerPolicy(aImageReferrerPolicy),
diff --git a/parser/html/nsHtml5TreeOpExecutor.h b/parser/html/nsHtml5TreeOpExecutor.h
@@ -262,7 +262,7 @@ class nsHtml5TreeOpExecutor final
const nsAString& aMedia, const nsAString& aSrcset,
const nsAString& aSizes,
const nsAString& aImageReferrerPolicy, bool aLinkPreload,
- const nsAString& aFetchPriority);
+ const nsAString& aFetchPriority, const nsAString& aType);
void PreloadOpenPicture();
@@ -317,6 +317,11 @@ class nsHtml5TreeOpExecutor final
*/
bool ShouldPreloadURI(nsIURI* aURI);
+ /**
+ * Returns true if the image type is supported.
+ */
+ bool ImageTypeSupports(const nsAString& aType);
+
ReferrerPolicy GetPreloadReferrerPolicy(const nsAString& aReferrerPolicy);
ReferrerPolicy GetPreloadReferrerPolicy(ReferrerPolicy aReferrerPolicy);
diff --git a/testing/web-platform/tests/preload/preload-with-unsupported-type.html b/testing/web-platform/tests/preload/preload-with-unsupported-type.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<title>Makes sure the resources with an unsupported type attribute should not be loaded</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ var imageLoaded = false;
+ var imageError = false;
+ var preloadCount = undefined;
+</script>
+<link rel="preload" href="resources/preload-count.py?action=image" as="image" type="not-a-mime" onerror="imageError=true" onload="imageLoaded=true">
+<body>
+<script>
+ setup({single_test: true});
+
+ function getPreloadCount() {
+ return new Promise(resolve => {
+ var script = document.createElement("script");
+ script.src = "resources/preload-count.py?action=result";
+ script.onload = resolve;
+ document.body.appendChild(script);
+ });
+ }
+
+ function check_result() {
+ assert_false(imageLoaded, "image should not trigger load event");
+ assert_false(imageLoaded, "image should not trigger error event");
+ getPreloadCount().then(() => {
+ assert_equals(preloadCount, 0, "should not try to load resource");
+ done();
+ });
+ }
+
+ window.addEventListener("load", function() {
+ step_timeout(check_result, 3000);
+ });
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/preload/resources/preload-count.py b/testing/web-platform/tests/preload/resources/preload-count.py
@@ -0,0 +1,17 @@
+import sys
+
+def main(request, response):
+ key = b"a8697ae7-c8cb-4dbd-a8ef-27111dc7042f"
+ count = request.server.stash.take(key)
+ if count is None:
+ count = 0
+
+ action = request.GET.first(b"action")
+ if action == b"result":
+ response.headers.append(b"Content-Type", b"text/javascript")
+ return "preloadCount = {0};".format(count)
+ else:
+ count += 1
+ request.server.stash.put(key, count)
+ response.status = 404
+ return 'No entry is found'