Skip to content

Commit fc485c3

Browse files
committed
[Float][Fizz][Fiber] support imagesrcset and imagesizes for ReactDOM.preload() (#26940)
For float methods the href argument is usually all we need to uniquely key the request. However when preloading responsive images it is possible that you may need more than one preload for differing imagesizes attributes. When using imagesrcset for preloads the href attribute acts more like a fallback href. For keying purposes the imagesrcset becomes the primary key conceptually. This change updates the keying logic for `ReactDOM.preload()` when you pass `{as: "image"}` 1. If `options.imageSrcSet` is a non-emtpy string the key is defined as `options.imageSrcSet + options.imageSizes`. The `href` argument is still required but does not participate in keying. 2. If `options.imageSrcSet` is empty, missing, or an invalid format the key is defined as the `href`. Changing the `options.imageSizes` does not affect the key as this option is inert when not using `options.imageSrcSet` Additionally, currently there is a bug in webkit (Safari) that causes preload links to fail to use imageSrcSet and fallback to href even when the browser will correctly resolve srcset on an `<img>` tag. Because the drawbacks of preloading the wrong image (href over imagesrcset) in a modern browser outweight the drawbacks of not preloading anything for responsive images in browsers that do not support srcset at all we will omit the `href` attribute whenever `options.imageSrcSet` is provided. We still require you provide an href since we want to be able to revert this behavior once all major browsers support it bug link: https://bugs.webkit.org/show_bug.cgi?id=231150 DiffTrain build for [fc929cf](fc929cf)
1 parent b437e4d commit fc485c3

22 files changed

+807
-487
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
86acc10f2596e1a6fe2fd57a5b325de85175800b
1+
fc929cf4ead35f99c4e9612a95e8a0bb8f5df25d

compiled/facebook-www/React-dev.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ if (
2727
}
2828
"use strict";
2929

30-
var ReactVersion = "18.3.0-www-modern-5fb44b73";
30+
var ReactVersion = "18.3.0-www-modern-58022814";
3131

3232
// ATTENTION
3333
// When adding new symbols to this file,

compiled/facebook-www/ReactART-dev.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
6969
return self;
7070
}
7171

72-
var ReactVersion = "18.3.0-www-modern-520e6c62";
72+
var ReactVersion = "18.3.0-www-modern-c8bee3bc";
7373

7474
var LegacyRoot = 0;
7575
var ConcurrentRoot = 1;

compiled/facebook-www/ReactART-prod.modern.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9858,7 +9858,7 @@ var slice = Array.prototype.slice,
98589858
return null;
98599859
},
98609860
bundleType: 0,
9861-
version: "18.3.0-www-modern-e8821afc",
9861+
version: "18.3.0-www-modern-12c03e2f",
98629862
rendererPackageName: "react-art"
98639863
};
98649864
var internals$jscomp$inline_1298 = {
@@ -9889,7 +9889,7 @@ var internals$jscomp$inline_1298 = {
98899889
scheduleRoot: null,
98909890
setRefreshHandler: null,
98919891
getCurrentFiber: null,
9892-
reconcilerVersion: "18.3.0-www-modern-e8821afc"
9892+
reconcilerVersion: "18.3.0-www-modern-12c03e2f"
98939893
};
98949894
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
98959895
var hook$jscomp$inline_1299 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

compiled/facebook-www/ReactDOM-dev.classic.js

Lines changed: 72 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -34178,7 +34178,7 @@ function createFiberRoot(
3417834178
return root;
3417934179
}
3418034180

34181-
var ReactVersion = "18.3.0-www-classic-3dccae4a";
34181+
var ReactVersion = "18.3.0-www-classic-00cc677f";
3418234182

3418334183
function createPortal$1(
3418434184
children,
@@ -42068,65 +42068,6 @@ function propNamesListJoin(list, combinator) {
4206842068
}
4206942069
}
4207042070

42071-
function validatePreloadArguments(href, options) {
42072-
{
42073-
if (!href || typeof href !== "string") {
42074-
var typeOfArg = getValueDescriptorExpectingObjectForWarning(href);
42075-
42076-
error(
42077-
"ReactDOM.preload() expected the first argument to be a string representing an href but found %s instead.",
42078-
typeOfArg
42079-
);
42080-
} else if (typeof options !== "object" || options === null) {
42081-
var _typeOfArg = getValueDescriptorExpectingObjectForWarning(options);
42082-
42083-
error(
42084-
'ReactDOM.preload() expected the second argument to be an options argument containing at least an "as" property' +
42085-
' specifying the Resource type. It found %s instead. The href for the preload call where this warning originated is "%s".',
42086-
_typeOfArg,
42087-
href
42088-
);
42089-
} else {
42090-
var as = options.as;
42091-
42092-
switch (as) {
42093-
// Font specific validation of options
42094-
case "font": {
42095-
if (options.crossOrigin === "use-credentials") {
42096-
error(
42097-
'ReactDOM.preload() was called with an "as" type of "font" and with a "crossOrigin" option of "use-credentials".' +
42098-
' Fonts preloading must use crossOrigin "anonymous" to be functional. Please update your font preload to omit' +
42099-
' the crossOrigin option or change it to any other value than "use-credentials" (Browsers default all other values' +
42100-
' to anonymous mode). The href for the preload call where this warning originated is "%s"',
42101-
href
42102-
);
42103-
}
42104-
42105-
break;
42106-
}
42107-
42108-
case "script":
42109-
case "style": {
42110-
break;
42111-
}
42112-
// We have an invalid as type and need to warn
42113-
42114-
default: {
42115-
var typeOfAs = getValueDescriptorExpectingEnumForWarning(as);
42116-
42117-
error(
42118-
'ReactDOM.preload() expected a valid "as" type in the options (second) argument but found %s instead.' +
42119-
" Please use one of the following valid values instead: %s. The href for the preload call where this" +
42120-
' warning originated is "%s".',
42121-
typeOfAs,
42122-
'"style", "font", or "script"',
42123-
href
42124-
);
42125-
}
42126-
}
42127-
}
42128-
}
42129-
}
4213042071
function validatePreinitArguments(href, options) {
4213142072
{
4213242073
if (!href || typeof href !== "string") {
@@ -42137,12 +42078,12 @@ function validatePreinitArguments(href, options) {
4213742078
typeOfArg
4213842079
);
4213942080
} else if (typeof options !== "object" || options === null) {
42140-
var _typeOfArg2 = getValueDescriptorExpectingObjectForWarning(options);
42081+
var _typeOfArg = getValueDescriptorExpectingObjectForWarning(options);
4214142082

4214242083
error(
4214342084
'ReactDOM.preinit() expected the second argument to be an options argument containing at least an "as" property' +
4214442085
' specifying the Resource type. It found %s instead. The href for the preload call where this warning originated is "%s".',
42145-
_typeOfArg2,
42086+
_typeOfArg,
4214642087
href
4214742088
);
4214842089
} else {
@@ -43755,7 +43696,35 @@ function preconnect$1(href, options) {
4375543696

4375643697
function preload$1(href, options) {
4375743698
{
43758-
validatePreloadArguments(href, options);
43699+
// TODO move this to ReactDOMFloat and expose a stricter function interface or possibly
43700+
// typed functions (preloadImage, preloadStyle, ...)
43701+
var encountered = "";
43702+
43703+
if (typeof href !== "string" || !href) {
43704+
encountered +=
43705+
"The `href` argument encountered was " +
43706+
getValueDescriptorExpectingObjectForWarning(href) +
43707+
".";
43708+
}
43709+
43710+
if (options == null || typeof options !== "object") {
43711+
encountered +=
43712+
"The `options` argument encountered was " +
43713+
getValueDescriptorExpectingObjectForWarning(options) +
43714+
".";
43715+
} else if (typeof options.as !== "string" || !options.as) {
43716+
encountered +=
43717+
"The `as` option encountered was " +
43718+
getValueDescriptorExpectingObjectForWarning(options.as) +
43719+
".";
43720+
}
43721+
43722+
if (encountered) {
43723+
error(
43724+
'ReactDOM.preload(): Expected two arguments, a non-empty `href` string and an `options` object with an `as` property valid for a `<link rel="preload" as="..." />` tag. %s',
43725+
encountered
43726+
);
43727+
}
4375943728
}
4376043729

4376143730
var ownerDocument = getDocumentForImperativeFloatMethods();
@@ -43765,13 +43734,42 @@ function preload$1(href, options) {
4376543734
href &&
4376643735
typeof options === "object" &&
4376743736
options !== null &&
43737+
typeof options.as === "string" &&
43738+
options.as &&
4376843739
ownerDocument
4376943740
) {
4377043741
var as = options.as;
43771-
var limitedEscapedHref =
43772-
escapeSelectorAttributeValueInsideDoubleQuotes(href);
4377343742
var preloadSelector =
43774-
'link[rel="preload"][as="' + as + '"][href="' + limitedEscapedHref + '"]'; // Some preloads are keyed under their selector. This happens when the preload is for
43743+
'link[rel="preload"][as="' +
43744+
escapeSelectorAttributeValueInsideDoubleQuotes(as) +
43745+
'"]';
43746+
43747+
if (as === "image") {
43748+
var imageSrcSet = options.imageSrcSet,
43749+
imageSizes = options.imageSizes;
43750+
43751+
if (typeof imageSrcSet === "string" && imageSrcSet !== "") {
43752+
preloadSelector +=
43753+
'[imagesrcset="' +
43754+
escapeSelectorAttributeValueInsideDoubleQuotes(imageSrcSet) +
43755+
'"]';
43756+
43757+
if (typeof imageSizes === "string") {
43758+
preloadSelector +=
43759+
'[imagesizes="' +
43760+
escapeSelectorAttributeValueInsideDoubleQuotes(imageSizes) +
43761+
'"]';
43762+
}
43763+
} else {
43764+
preloadSelector +=
43765+
'[href="' +
43766+
escapeSelectorAttributeValueInsideDoubleQuotes(href) +
43767+
'"]';
43768+
}
43769+
} else {
43770+
preloadSelector +=
43771+
'[href="' + escapeSelectorAttributeValueInsideDoubleQuotes(href) + '"]';
43772+
} // Some preloads are keyed under their selector. This happens when the preload is for
4377543773
// an arbitrary type. Other preloads are keyed under the resource key they represent a preload for.
4377643774
// Here we figure out which key to use to determine if we have a preload already.
4377743775

@@ -43817,14 +43815,20 @@ function preload$1(href, options) {
4381743815

4381843816
function preloadPropsFromPreloadOptions(href, as, options) {
4381943817
return {
43820-
href: href,
4382143818
rel: "preload",
4382243819
as: as,
43820+
// There is a bug in Safari where imageSrcSet is not respected on preload links
43821+
// so we omit the href here if we have imageSrcSet b/c safari will load the wrong image.
43822+
// This harms older browers that do not support imageSrcSet by making their preloads not work
43823+
// but this population is shrinking fast and is already small so we accept this tradeoff.
43824+
href: as === "image" && options.imageSrcSet ? undefined : href,
4382343825
crossOrigin: as === "font" ? "" : options.crossOrigin,
4382443826
integrity: options.integrity,
4382543827
type: options.type,
4382643828
nonce: options.nonce,
43827-
fetchPriority: options.fetchPriority
43829+
fetchPriority: options.fetchPriority,
43830+
imageSrcSet: options.imageSrcSet,
43831+
imageSizes: options.imageSizes
4382843832
};
4382943833
}
4383043834

0 commit comments

Comments
 (0)